1 unit Unit1; 2 3 interface 4 5 uses 6 WinSock2, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 7 Dialogs, StdCtrls, ExtCtrls; 8 9 type 10 //单IO数据结构 11 LPER_IO_OPERATION_DATA = ^TPER_IO_OPERATION_DATA; 12 TPER_IO_OPERATION_DATA = packed record 13 Overlapped: WSAOverlapped; 14 DataBuf: WSABuf; 15 Buff: array [0..10] of Char; 16 BytesSend: DWORD; 17 BytesRecv: DWORD; 18 end; 19 20 //单句柄数据结构 21 LPER_HANDLE_DATA = ^TPER_HANDLE_DATA; 22 TPER_HANDLE_DATA = packed record 23 Socket: TSocket; 24 end; 25 26 TListenThread = class(TThread) 27 private 28 protected 29 procedure Execute;override; 30 public 31 constructor Create; 32 end; 33 34 TForm1 = class(TForm) 35 Memo1: TMemo; 36 Button1: TButton; 37 procedure FormCreate(Sender: TObject); 38 private 39 { Private declarations } 40 public 41 { Public declarations } 42 end; 43 44 var 45 Form1: TForm1; 46 ServerSocket: TSocket; 47 48 implementation 49 50 {$R *.dfm} 51 52 //工作者线程 53 function WorkThread(CompletionPortID: Pointer):DWORD; stdcall; 54 var 55 CompletionPort: THandle; 56 BytesTransferred: DWORD; 57 PerHandleData: LPER_HANDLE_DATA; 58 PerIOData: LPER_IO_OPERATION_DATA; 59 Flags: DWORD; 60 RecvBytes: DWORD; 61 begin 62 CompletionPort:= THandle(CompletionPortID); 63 64 while True do 65 begin 66 //获取完成端口上的队列的完成状态 67 GetQueuedCompletionStatus(CompletionPort, BytesTransferred, DWORD(PerHandleData), 68 POverlapped(PerIOData), INFINITE); 69 70 //判断是客户端发来的数据还是服务端发出的数据 71 if PerIOData.BytesRecv = 0 then 72 begin 73 PerIOData.BytesRecv:= BytesTransferred; 74 PerIOData.BytesSend:= 0; 75 end else 76 PerIOData.BytesSend:= PerIOData.BytesSend + BytesTransferred; 77 78 if PerIOData.BytesRecv > PerIOData.BytesSend then 79 begin 80 ZeroMemory(@(PerIOData.Overlapped), SizeOf(WSAOverlapped)); 81 PerIOData.DataBuf.buf:= PerIOData.Buff + PerIOData.BytesSend; 82 PerIOData.DataBuf.len:= PerIOData.BytesRecv - PerIOData.BytesSend; 83 84 //显示收到的数据,这样做是不安全的,示例而已 :) 85 Form1.Memo1.Lines.Add(PerIOData.Buff); 86 end; 87 88 //重置数据 89 PerIOData.BytesRecv:= 0; 90 PerIOData.DataBuf.len:= 22; 91 PerIOData.DataBuf.buf:= @PerIOData.Buff; 92 93 //再次投递 94 WSARecv(PerHandleData.Socket, @(PerIOData.DataBuf), 1, RecvBytes, Flags, 95 @(PerIOData.Overlapped), nil); 96 end; 97 end; 98 99 { TWorkThread } 100 101 constructor TListenThread.Create; 102 begin 103 inherited Create(False); 104 FreeOnTerminate:= True; 105 end; 106 107 procedure TListenThread.Execute; 108 var 109 WSData: TWSAData; 110 CompletionPort: THandle; 111 lpSystemInfo: TSystemInfo; 112 Idx: Integer; 113 ThreadID: DWORD; 114 LocalAddr: TSockaddr; 115 ClientAddr: TSockaddr; 116 ClientSocket: TSocket; 117 118 PER_HANDLE_DATA: LPER_HANDLE_DATA; 119 PER_IO_OPERATION_DATA: LPER_IO_OPERATION_DATA; 120 121 RecvBytes: DWORD; 122 Flags: DWORD; 123 begin 124 inherited; 125 126 //初始化Winsock 127 WSAStartUp($202, WSData); 128 //创建完成端口 129 CompletionPort:= CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); 130 //根据处理器数量创建工作者线程的数量(网上流传的说法是线程数量=cpu数量*2+2) 131 GetSystemInfo(lpSystemInfo); 132 for Idx := 1 to lpSystemInfo.dwNumberOfProcessors *2 + 2 do 133 //创建工作者线程,并将完成端口句柄传递给线程 134 CreateThread(nil, 0, @WorkThread, Pointer(CompletionPort), 0, ThreadID); 135 //创建监听套接字 136 ServerSocket:= WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nil, 0, WSA_FLAG_OVERLAPPED); 137 //设置LocalAddr的参数 138 LocalAddr.sin_family:= AF_INET; //IPV4族 139 LocalAddr.sin_addr.S_addr:= INADDR_ANY;//这里不能写Inet_addr('127.0.0.1'),否则会绑定失败,不清楚原因是什么; 140 LocalAddr.sin_port:= Htons(1077); //Host To Net Short,主机字节顺序转为网络字节顺序 141 //绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数 142 Bind(ServerSocket, @LocalAddr, SizeOf(LocalAddr)); 143 //开始监听 144 Listen(ServerSocket, 5); 145 146 while not Terminated do 147 begin 148 ClientSocket:= WSAAccept(ServerSocket, ClientAddr, nil, nil, 0); 149 //创建TPER_HANDLE_DATA结构的变量保存客户端Socket 150 PER_HANDLE_DATA:= LPER_HANDLE_DATA(GlobalAlloc(GPTR, SizeOf(TPER_HANDLE_DATA))); 151 PER_HANDLE_DATA.Socket:= ClientSocket; 152 //把完成端口和客户端套接字关联起来 153 CreateIOCompletionPort(ClientSocket, CompletionPort, DWORD(PER_HANDLE_DATA), 0); 154 //创建TPER_IO_OPERATION_DATA结构的变量,关联WSARecv函数 155 PER_IO_OPERATION_DATA:= LPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, SizeOf(TPER_IO_OPERATION_DATA))); 156 ZeroMemory(@PER_IO_OPERATION_DATA.Overlapped, SizeOf(WSAOverlapped)); 157 PER_IO_OPERATION_DATA.BytesSend:= 0; 158 PER_IO_OPERATION_DATA.BytesRecv:= 0; 159 PER_IO_OPERATION_DATA.DataBuf.len:= 22; 160 PER_IO_OPERATION_DATA.DataBuf.buf:= @PER_IO_OPERATION_DATA.Buff; 161 162 WSARecv(ClientSocket, @(PER_IO_OPERATION_DATA.DataBuf), 1, RecvBytes, 163 Flags, @(PER_IO_OPERATION_DATA.Overlapped), nil); 164 end; 165 end; 166 167 168 procedure TForm1.FormCreate(Sender: TObject); 169 begin 170 //创建监听线程 171 TListenThread.Create(); 172 end; 173 174 175 end.
该代码仅仅是练习IOCP的函数使用和大体的实现步骤,里面很多细节并不是唯一的方法,例如单IO数据结构和单句柄数据结构的定义等等。