Delphi-IOCP学习笔记三====工作线程和Listener
接第一次代碼繼續分析
usesJwaWinsock2, Windows, SysUtils;constDATA_BUFSIZE = 1024;IO_TYPE_Accept = 1;IO_TYPE_Recv = 2;type//(1):單IO數據結構LPVOID = Pointer;LPPER_IO_OPERATION_DATA = ^PER_IO_OPERATION_DATA ;PER_IO_OPERATION_DATA = packed recordOverlapped: OVERLAPPED;IO_TYPE: Cardinal;DataBuf: TWSABUF;Buffer: array [0..1024] of CHAR;end;剛開始結存iocp的時候可能無法理解為什么要申明這樣一個結構。
解釋下,這個結構是GetQueuedCompletionStatus,PostQueuedCompletionStatus,WSARecv,WSASend,時需要用到一個POverlapped類型的參數。
也許還會有疑惑,為什么不直接使用系統自帶的類型呢?
POverlapped = ^TOverlapped;
_OVERLAPPED = record
? Internal: DWORD;
? InternalHigh: DWORD;
? Offset: DWORD;
? OffsetHigh: DWORD;
? hEvent: THandle;
end;
///我可以解釋下.是為了在PostQueuedCompletionStatus,WSARecv,WSASend盡可能多傳遞一下信息給GetQueuedCompletionStatus,所以一般都會擴展這一機構體
//再啰嗦下。定義的結構體,Overlapped: OVERLAPPED;必須放在第一個.你懂的。
//PostQueuedCompletionStatus,WSARecv,WSASend會觸發工作線程的GetQueuedCompletionStatus返回<上一筆記有提到>
?
?
?
?
?
下面片段是Listen過程
//下面循環進行循環獲取客戶端的請求。while (TRUE) dobegin//當客戶端有連接請求的時候,WSAAccept函數會新創建一個套接字cSocket。這個套接字就是和客戶端通信的時候使用的套接字。cSocket:= WSAAccept(sSocket, nil, nil, nil, 0);//判斷cSocket套接字創建是否成功,如果不成功則退出。if (cSocket= SOCKET_ERROR) thenbeginclosesocket(sSocket);exit;end;//將套接字、完成端口綁定在一起。// 最開始的時候沒有明白為什么還要調用一次createIoCompletionPort//// 后來經過google,和測試//// 是將新的套接字(socket)加入到iocp端口<綁定>// 這樣工作線程才能處理這個套接字(socket)的數據包//如果把下面注釋掉,WSARecv這個套接字時,GetQueuedCompletionStatus無法處理到收到的數據包 // 2013年4月19日 09:56:00 // 注意第三個參數也需要進行綁定, 否則在工作線程中GetQueuedCompletionStatus時completionKey會取不到cSocket值 lvPerIOPort := CreateIoCompletionPort(cSocket, lvIOPort, cSocket, 0);if (lvPerIOPort = 0) thenbeginExit;end;//初始化數據包PerIoData := LPPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA)));ZeroMemory(@PerIoData.Overlapped, sizeof(OVERLAPPED));//數據包中的IO類型:有連接請求PerIoData.IO_TYPE := IO_TYPE_Accept;//通知工作線程,有新的套接字連接<第三個參數>PostQueuedCompletionStatus(lvIOPort, 0, cSocket, POverlapped(PerIOData));end;?
?
下面是IOCP工作線程
?
function ServerWorkerThread(pData:Pointer): Integer; stdcall; varCompletionPort:THANDLE;BytesTransferred:Cardinal;PerIoData:LPPER_IO_OPERATION_DATA;cSocket:TSocket;Flags:Cardinal;RecvBytes:Cardinal;lvResultStatus:BOOL;lvRet:Integer;beginCompletionPort:=THandle(pData);//得到創建線程是傳遞過來的IOCPwhile(TRUE) dobegin//工作者線程會停止到GetQueuedCompletionStatus函數處,直到接受到數據為止lvResultStatus := GetQueuedCompletionStatus(CompletionPort,BytesTransferred,cSocket,POverlapped(PerIoData), INFINITE); if (lvResultStatus = False) thenbegin//當客戶端連接斷開或者客戶端調用closesocket函數的時候,函數GetQueuedCompletionStatus會返回錯誤。如果我們加入心跳后,在這里就可以來判斷套接字是否依然在連接。if cSocket<>0 thenbeginclosesocket(cSocket);end;if PerIoData<>nil thenbeginGlobalFree(DWORD(PerIoData));end;continue;end;if PerIoData = nil thenbeginclosesocket(cSocket);Break;end else if (PerIoData<>nil) thenbeginshutdown(PerHandleData.Socket, 1);if PerIoData.IO_TYPE = IO_TYPE_Accept then //連接請求beginGlobalFree(DWORD(PerIoData));end else if PerIoData.IO_TYPE = IO_TYPE_Recv thenbegin 可以在這里處理數據…… GlobalFree(DWORD(PerIoData));end;/分配內存<可以加入內存池>PerIoData := LPPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA)));ZeroMemory(@PerIoData.Overlapped, sizeof(OVERLAPPED));Flags := 0;/進入投遞收取動作PerIoData.IO_TYPE := IO_TYPE_Recv;PerIoData.DataBuf.len:=DATA_BUFSIZE;ZeroMemory(@PerIoData.Buffer,sizeof(@PerIoData.Buffer));PerIoData.DataBuf.buf := @PerIoData.Buffer;/異步收取數據WSARecv(cSocket,@PerIoData.DataBuf,1,RecvBytes,Flags,@PerIoData^, nil);if (WSAGetLastError() <> ERROR_IO_PENDING) thenbeginclosesocket(cSocket);if PerIoData <> nil thenbeginGlobalFree(DWORD(PerIoData));end;Continue;end;end;end; end;總結
以上是生活随笔為你收集整理的Delphi-IOCP学习笔记三====工作线程和Listener的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java怎么抛出异常
- 下一篇: 行、重复-SAP HANA 集合操作 U