unit Unit1; interface uses WinSock2, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type //单IO数据结构 LPER_IO_OPERATION_DATA = ^TPER_IO_OPERATION_DATA; TPER_IO_OPERATION_DATA = packed record Overlapped: WSAOverlapped; DataBuf: WSABuf; Buff: array [0..10] of Char; BytesSend: DWORD; BytesRecv: DWORD; end; //单句柄数据结构 LPER_HANDLE_DATA = ^TPER_HANDLE_DATA; TPER_HANDLE_DATA = packed record Socket: TSocket; end; TListenThread = class(TThread) private protected procedure Execute;override; public constructor Create; end; TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; ServerSocket: TSocket; implementation {$R *.dfm} //工作者线程 function WorkThread(CompletionPortID: Pointer):DWORD; stdcall; var CompletionPort: THandle; BytesTransferred: DWORD; PerHandleData: LPER_HANDLE_DATA; PerIOData: LPER_IO_OPERATION_DATA; Flags: DWORD; RecvBytes: DWORD; begin CompletionPort:= THandle(CompletionPortID); while True do begin //获取完成端口上的队列的完成状态 GetQueuedCompletionStatus(CompletionPort, BytesTransferred, DWORD(PerHandleData), POverlapped(PerIOData), INFINITE); //判断是客户端发来的数据还是服务端发出的数据 if PerIOData.BytesRecv = 0 then begin PerIOData.BytesRecv:= BytesTransferred; PerIOData.BytesSend:= 0; end else PerIOData.BytesSend:= PerIOData.BytesSend + BytesTransferred; if PerIOData.BytesRecv > PerIOData.BytesSend then begin ZeroMemory(@(PerIOData.Overlapped), SizeOf(WSAOverlapped)); PerIOData.DataBuf.buf:= PerIOData.Buff + PerIOData.BytesSend; PerIOData.DataBuf.len:= PerIOData.BytesRecv - PerIOData.BytesSend; //显示收到的数据,这样做是不安全的,示例而已 :) Form1.Memo1.Lines.Add(PerIOData.Buff); end; //重置数据 PerIOData.BytesRecv:= 0; PerIOData.DataBuf.len:= 22; PerIOData.DataBuf.buf:= @PerIOData.Buff; //再次投递 WSARecv(PerHandleData.Socket, @(PerIOData.DataBuf), 1, RecvBytes, Flags, @(PerIOData.Overlapped), nil); end; end; { TWorkThread } constructor TListenThread.Create; begin inherited Create(False); FreeOnTerminate:= True; end; procedure TListenThread.Execute; var WSData: TWSAData; CompletionPort: THandle; lpSystemInfo: TSystemInfo; Idx: Integer; ThreadID: DWORD; LocalAddr: TSockaddr; ClientAddr: TSockaddr; ClientSocket: TSocket; PER_HANDLE_DATA: LPER_HANDLE_DATA; PER_IO_OPERATION_DATA: LPER_IO_OPERATION_DATA; RecvBytes: DWORD; Flags: DWORD; begin inherited; //初始化Winsock WSAStartUp($202, WSData); //创建完成端口 CompletionPort:= CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); //根据处理器数量创建工作者线程的数量(网上流传的说法是线程数量=cpu数量*2+2) GetSystemInfo(lpSystemInfo); for Idx := 1 to lpSystemInfo.dwNumberOfProcessors *2 + 2 do //创建工作者线程,并将完成端口句柄传递给线程 CreateThread(nil, 0, @WorkThread, Pointer(CompletionPort), 0, ThreadID); //创建监听套接字 ServerSocket:= WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nil, 0, WSA_FLAG_OVERLAPPED); //设置LocalAddr的参数 LocalAddr.sin_family:= AF_INET; //IPV4族 LocalAddr.sin_addr.S_addr:= INADDR_ANY;//这里不能写Inet_addr('127.0.0.1'),否则会绑定失败,不清楚原因是什么; LocalAddr.sin_port:= Htons(1077); //Host To Net Short,主机字节顺序转为网络字节顺序 //绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数 Bind(ServerSocket, @LocalAddr, SizeOf(LocalAddr)); //开始监听 Listen(ServerSocket, 5); while not Terminated do begin ClientSocket:= WSAAccept(ServerSocket, ClientAddr, nil, nil, 0); //创建TPER_HANDLE_DATA结构的变量保存客户端Socket PER_HANDLE_DATA:= LPER_HANDLE_DATA(GlobalAlloc(GPTR, SizeOf(TPER_HANDLE_DATA))); PER_HANDLE_DATA.Socket:= ClientSocket; //把完成端口和客户端套接字关联起来 CreateIOCompletionPort(ClientSocket, CompletionPort, DWORD(PER_HANDLE_DATA), 0); //创建TPER_IO_OPERATION_DATA结构的变量,关联WSARecv函数 PER_IO_OPERATION_DATA:= LPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, SizeOf(TPER_IO_OPERATION_DATA))); ZeroMemory(@PER_IO_OPERATION_DATA.Overlapped, SizeOf(WSAOverlapped)); PER_IO_OPERATION_DATA.BytesSend:= 0; PER_IO_OPERATION_DATA.BytesRecv:= 0; PER_IO_OPERATION_DATA.DataBuf.len:= 22; PER_IO_OPERATION_DATA.DataBuf.buf:= @PER_IO_OPERATION_DATA.Buff; WSARecv(ClientSocket, @(PER_IO_OPERATION_DATA.DataBuf), 1, RecvBytes, Flags, @(PER_IO_OPERATION_DATA.Overlapped), nil); end; end; procedure TForm1.FormCreate(Sender: TObject); begin //创建监听线程 TListenThread.Create(); end; end.
该代码仅仅是练习IOCP的函数使用和大体的实现步骤,里面很多细节并不是唯一的方法,例如单IO数据结构和单句柄数据结构的定义等等。