大体原理: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: beginend;
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;