学习笔记之WSAAsyncSelect模式

大体原理:WSAAsyncSelect模式允许以windows消息的形式接收网络事件通知。这个模式是为了适应windows消息驱动环境而设置的,对性能要求不高的网络应用程序可采用此模式。

要注意的地方:网络事件消息抵达消息处理函数后,应用程序首先检查Lparam参数的高位,以判断在套接字上发生了网络错误。宏WSAGETSELECTERROR返回高字节包含的错误信息。若应用程序发现套接字上没有产生任何错误便可用宏WSAGETSELECTEVENT读取LPARAM参数的低字位确定发生的网络事件。

优缺点:WSAAsyncSelect模型最突出的特点是与windows的消息驱动机制融在了一起,这使得开发带GUI界面的网络程序变得很简单。但是如果连接增加,单个WINDOWS函数处理上千个客户请求时,服务器性能势必发受到影响。

主要函数:WSAAsyncSelect。

主要参数:发送的通知码含义:

FD_READ:套接字接收到对方发送过来的数据包,表明可以去读套接字了。

FD_WRITE: 数据缓冲区满后再次变空时,通知应用 程序可以继续发送数据了。

           注:send函数先将要发送的数据放入缓冲区中后,由windows负责将数据发送出去。如果缓冲区满了, 则不能发送数据了。当FD_WRITE后,可以再发送数据。

FD_ACCEPT,FD_CONNECT,FD_CLOSE。看单词就明白了。

DELPHI代码:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,JwaWinsock2;
const
  WM_SOCKET=WM_USER+100;
type
  TForm1 = class(TForm)
    mmo1: TMemo;
    btn1: TButton;
    mmo2: TMemo;
    btn2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
    procedure btn2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    procedure Self_WndProc(var Msg: TMessage);  //拦截窗体的所有消息
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Oldwindowproc:TWndMethod;
  sListen:TSocket;
  WSData: TWSAData;
implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  OldWindowProc   := Self.WindowProc; //保存原来的函数地址;
  Self.WindowProc :=Self_WndProc;     //指定新的函数地址;
  WSAStartup($0202, WSData);
  sListen:=INVALID_SOCKET;
  sListen:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

end;
procedure TForm1.Self_WndProc(var msg:TMessage);
var
  sClient, sEvent: TSocket;
  addrRemote: TSockAddrIn;
  addrlen:Integer;
  szText:array[0..255] of AnsiChar;
begin
  if Msg.Msg=WM_SOCKET then
    begin
      sEvent:=Msg.WParam;
      if WSAGETASYNCERROR(Msg.LParam)<>0 then
        begin
          closesocket(sEvent);
        end
      else
        begin
          case WSAGETSELECTEVENT(Msg.LParam) of
            FD_ACCEPT:  //当监听到套接字有连接进入时。
              begin
                addrlen:=SizeOf(addrRemote);
                sClient:=accept(sEvent,psockaddr(@addrRemote),@addrlen);
                WSAAsyncSelect(sClient,Self.Handle,WM_SOCKET,FD_ACCEPT or FD_CLOSE or FD_READ or FD_WRITE);
                mmo1.Lines.Add('当前连接IP信息:'+strpas(inet_ntoa(addrRemote.sin_addr)));
              end;
            FD_WRITE:
              begin

              end;
            FD_READ:
              begin
                if recv(sEvent,szText,SizeOf(szText),0)>0 then
                     begin
                       mmo2.Lines.Add('接收的数据是:'+strpas(sztext));
                     end
                   else
                     begin
                       closesocket(sEvent);
                     end;
              end;
            FD_CLOSE:
               begin
                 closesocket(sEvent);
                 mmo1.Lines.Add('用户断开连接。');
               end;
          end;
        end;
    end;
   OldWindowProc(Msg);
end;
procedure TForm1.btn1Click(Sender: TObject);    //启动临听服务
var
  sins:sockaddr_in;
begin
 if sListen<>INVALID_SOCKET then
   begin
     Sins.sin_family  :=AF_INET;
     sins.sin_port    :=htons(4567);
     sins.sin_addr.S_addr:=INADDR_ANY;
     if bind(sListen,@sins,SizeOf(sins))=SOCKET_ERROR then
      begin
         ShowMessage('无法进行端口绑定。');
         Exit;
      end;
     WSAAsyncSelect(sListen,Self.Handle,WM_SOCKET,FD_ACCEPT or FD_CLOSE or FD_READ or FD_WRITE);
     listen(sListen,5);
     btn1.Enabled:=False;
   end;

posted @ 2011-04-29 17:22  菜程序员  阅读(1879)  评论(0编辑  收藏  举报