《delphi中完成端口(摘自掰掰开发)》由会员分享,可在线阅读,更多相关《delphi中完成端口(摘自掰掰开发)(18页珍藏版)》请在金锄头文库上搜索。
1、DELPHI中完成端口(IOCP)的简单分析出自 掰掰开发-http:/ DELPHI开发网络代码已经有一段时间了!我发现在网上用 VC来实现完成端口(IOCP)的代码很多,但是使用 DELPHI来实现的就比较少了。对 IOCP讲的清楚的就更少了。在这里我把自己编写 DELPHI下的 IOCP写出来,希望对刚学完成端口的朋友有个帮助。首先我们来了解一些在使用 IOCP的时候需要使用的一些结构!(1):单 IO数据结构LPVOID = Pointer;LPPER_IO_OPERATION_DATA = PER_IO_OPERATION_DATA ;PER_IO_OPERATION_DATA =
2、packed recordOverlapped: OVERLAPPED;DataBuf: TWSABUF;Buffer: array 0.1024 of CHAR;BytesSEND: DWORD;BytesRECV: DWORD;end;上面的结构中 Overlapped: OVERLAPPED;和 DataBuf: TWSABUF;是固定的结构类型。Buffer: array 0.1024 of CHAR;是用来保存接受数据的缓存。BytesSEND: DWORD;用来标志发送数据的长度。BytesRECV: DWORD;用来标志接受数据的长度。因为完成端口的工作者线程可以接受到来自客户端
3、的数据,同时还可以接受到自己发送给客户端的数据,所以我们使用BytesSEND,BytesRECV 变量来说是用来区分这次的数据是来自客户端的数据还是自己发送出去的数据。详细的使用方法,我会在下面详细说明。(2):“单句柄数据结构”LPPER_HANDLE_DATA = PER_HANDLE_DATA;PER_HANDLE_DATA = packed recordSocket: TSocket;end;下来我从编写一个完成端口的为例说明。if WSAStartUp($202, wsData) 0 thenbeginWSACleanup();end;加载 SOCKET。我使用的是 2.2版为了后
4、面方便加入心跳。CompletionPort:=CreateIOCompletionPort(INVALID_HANDLE_VALUE,0,0,0);创建一个完成端口。GetSystemInfo(LocalSI);for I:=0 to LocalSI.dwNumberOfProcessors * 2 -1 dobeginhThread := CreateThread(nil, 0, ServerWorkerThread, Pointer(CompletionPort),0, ThreadID);if (hThread = 0) thenbeginExit;end;CloseHandle(hT
5、hread);end;根据 CPU的数量创建 CPU*2数量的工作者线程。Listensc:=WSASocket(AF_INET,SOCK_STREAM,0,Nil,0,WSA_FLAG_OVERLAPPED);if Listensc=SOCKET_ERROR thenbeginclosesocket(Listensc);WSACleanup();end;sto.sin_family:=AF_INET;sto.sin_port:=htons(5500);sto.sin_addr.s_addr:=htonl(INADDR_ANY);if bind(Listensc,sto,sizeof(sto)
6、=SOCKET_ERROR thenbeginclosesocket(Listensc);end;listen(Listensc,20);创建一个套接字,将此套接字和一个端口绑定并监听此端口。while (TRUE) dobeginAcceptsc:= WSAAccept(Listensc, nil, nil, nil, 0);当客户端有连接请求的时候,WSAAccept 函数会新创建一个套接字 Acceptsc。这个套接字就是和客户端通信的时候使用的套接字。if (Acceptsc= SOCKET_ERROR) thenbeginclosesocket(Listensc);exit;end;
7、判断 Acceptsc套接字创建是否成功,如果不成功则退出。PerHandleData := LPPER_HANDLE_DATA (GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA);if (PerHandleData = nil) thenbeginexit;end;PerHandleData.Socket := Acceptsc;创建一个“单句柄数据结构”将 Acceptsc套接字绑定。if (CreateIoCompletionPort(Acceptsc, CompletionPort, DWORD(PerHandleData), 0) = 0) then
8、 beginexit;end;将套接字、完成端口和“单句柄数据结构”三者绑定在一起。PerIoData := LPPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA);if (PerIoData = nil) thenbeginexit;end;ZeroMemory(PerIoData.Overlapped, sizeof(OVERLAPPED);PerIoData.BytesSEND := 0;PerIoData.BytesRECV := 0;PerIoData.DataBuf.len := 1024;Pe
9、rIoData.DataBuf.buf := PerIoData.Buffer;Flags := 0;创建一个“单 IO数据结构”其中将 PerIoData.BytesSEND 和 PerIoData.BytesRECV 均设置成 0。说明此“单 IO数据结构”是用来接受的。if (WSARecv(Acceptsc, (PerIoData.DataBuf), 1, RecvBytes, Flags,(PerIoData.Overlapped), nil) = SOCKET_ERROR) thenbeginif (WSAGetLastError() ERROR_IO_PENDING) thenb
10、egin/最近在检查代码的时候发现以前这里只是使用 Exit来退出是不正确的。这里需要删除申请的单 IO数据结构,否子会出现内存泄露。 (2008 年 3月 24日)/Exit;closesocket(AcceptSc);if PerIoData nil thenbeginGlobalFree(DWORD(PerIoData);end;Continue;endend;用此“单 IO数据结构”来接受 Acceptsc套接字的数据。end;创建 IOCP的工作已经完成,下一次我将写 IOCP的工作者线程的处理方法,谢谢!今天我写一下关于 DELPHI编写完成端口(IOCP)的工作者线程中的东西。希
11、望各位能提出批评意见。上次我写了关于常见 IOCP的代码,对于 IOCP来说,接受到客户端发送过来和自己发送出去的数据都是从工作者线程中得到。代码和解释如下:function ServerWorkerThread(CompletionPortID: Pointer):Integer;stdcall;begin CompletionPort:=THANDLE(CompletionPortID);/得到创建线程是传递过来的 IOCPwhile(TRUE) dobegin/工作者线程会停止到 GetQueuedCompletionStatus函数处,直到接受到数据为止if (GetQueuedCom
12、pletionStatus(CompletionPort, BytesTransferred,DWORD(PerHandleData), POverlapped(PerIoData), INFINITE) = False) thenbegin/当客户端连接断开或者客户端调用 closesocket函数的时候,函数GetQueuedCompletionStatus会返回错误。如果我们加入心跳后,在这里就可以来判断套接字是否依然在连接。if PerHandleDatanil thenbeginclosesocket(PerHandleData.Socket);GlobalFree(DWORD(Pe
13、rHandleData);end;if PerIoDatanil thenbeginGlobalFree(DWORD(PerIoData);end;continue;end;if (BytesTransferred = 0) thenbegin/当客户端调用 shutdown函数来从容断开的时候,我们可以在这里进行处理。if PerHandleDatanil thenbeginTempSc:=PerHandleData.Socket;shutdown(PerHandleData.Socket,1);closesocket(PerHandleData.Socket);GlobalFree(DWO
14、RD(PerHandleData);end;if PerIoDatanil thenbeginGlobalFree(DWORD(PerIoData);end;continue;end;/在上一篇中我们说到 IOCP可以接受来自客户端的数据和自己发送出去的数据,两种数据的区别在于我们定义的结构成员 BytesRECV和 BytesSEND的值。所以下面我们来判断数据的来自方向。因为我们发送出去数据的时候我们设置了结构成员 BytesSEND。所以如果 BytesRECV=0同时 BytesSEND=0那么此数据就是我们接受到的客户端数据。 (这种区分方法不是唯一的,个人可以有自己的定义方法。只要
15、可以区分开数据来源就可以。 )if (PerIoData.BytesRECV = 0) and (PerIoData.BytesSEND = 0) thenbeginPerIoData.BytesRECV := BytesTransferred;PerIoData.BytesSEND := 0;endelsebeginPerIoData.BytesSEND := BytesTransferred;PerIoData.BytesRECV := 0;end;/当是接受来自客户端的数据是,我们进行数据的处理。if (PerIoData.BytesRECV PerIoData.BytesSEND) t
16、henbeginPerIoData.DataBuf.buf := PerIoData.Buffer + PerIoData.BytesSEND;PerIoData.DataBuf.len := PerIoData.BytesRECV - PerIoData.BytesSEND;/这时变量 PerIoData.Buffer就是接受到的客户端数据。数据的长度是PerIoData.DataBuf.len 你可以对数据进行相关的处理了。/./当我们将数据处理完毕以后,应该将此套接字设置为结束状态,同时初始化和它绑定在一起的数据结构。ZeroMemory(PerIoData.Overlapped), sizeof(OVERLAPPED);PerIoData.BytesRECV := 0;Flags := 0;ZeroMemory(PerIoData.Overlapped), sizeof(OVERLAPPED