Delphi-IOCP学习笔记三====工作线程和Listener
接第一次代碼繼續(xù)分析
usesJwaWinsock2, Windows, SysUtils;constDATA_BUFSIZE = 1024;IO_TYPE_Accept = 1;IO_TYPE_Recv = 2;type//(1):單IO數(shù)據(jù)結(jié)構(gòu)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;剛開始結(jié)存iocp的時(shí)候可能無法理解為什么要申明這樣一個(gè)結(jié)構(gòu)。
解釋下,這個(gè)結(jié)構(gòu)是GetQueuedCompletionStatus,PostQueuedCompletionStatus,WSARecv,WSASend,時(shí)需要用到一個(gè)POverlapped類型的參數(shù)。
也許還會(huì)有疑惑,為什么不直接使用系統(tǒng)自帶的類型呢?
POverlapped = ^TOverlapped;
_OVERLAPPED = record
? Internal: DWORD;
? InternalHigh: DWORD;
? Offset: DWORD;
? OffsetHigh: DWORD;
? hEvent: THandle;
end;
///我可以解釋下.是為了在PostQueuedCompletionStatus,WSARecv,WSASend盡可能多傳遞一下信息給GetQueuedCompletionStatus,所以一般都會(huì)擴(kuò)展這一機(jī)構(gòu)體
//再啰嗦下。定義的結(jié)構(gòu)體,Overlapped: OVERLAPPED;必須放在第一個(gè).你懂的。
//PostQueuedCompletionStatus,WSARecv,WSASend會(huì)觸發(fā)工作線程的GetQueuedCompletionStatus返回<上一筆記有提到>
?
?
?
?
?
下面片段是Listen過程
//下面循環(huán)進(jìn)行循環(huán)獲取客戶端的請(qǐng)求。while (TRUE) dobegin//當(dāng)客戶端有連接請(qǐng)求的時(shí)候,WSAAccept函數(shù)會(huì)新創(chuàng)建一個(gè)套接字cSocket。這個(gè)套接字就是和客戶端通信的時(shí)候使用的套接字。cSocket:= WSAAccept(sSocket, nil, nil, nil, 0);//判斷cSocket套接字創(chuàng)建是否成功,如果不成功則退出。if (cSocket= SOCKET_ERROR) thenbeginclosesocket(sSocket);exit;end;//將套接字、完成端口綁定在一起。// 最開始的時(shí)候沒有明白為什么還要調(diào)用一次createIoCompletionPort//// 后來經(jīng)過google,和測(cè)試//// 是將新的套接字(socket)加入到iocp端口<綁定>// 這樣工作線程才能處理這個(gè)套接字(socket)的數(shù)據(jù)包//如果把下面注釋掉,WSARecv這個(gè)套接字時(shí),GetQueuedCompletionStatus無法處理到收到的數(shù)據(jù)包 // 2013年4月19日 09:56:00 // 注意第三個(gè)參數(shù)也需要進(jìn)行綁定, 否則在工作線程中GetQueuedCompletionStatus時(shí)completionKey會(huì)取不到cSocket值 lvPerIOPort := CreateIoCompletionPort(cSocket, lvIOPort, cSocket, 0);if (lvPerIOPort = 0) thenbeginExit;end;//初始化數(shù)據(jù)包PerIoData := LPPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA)));ZeroMemory(@PerIoData.Overlapped, sizeof(OVERLAPPED));//數(shù)據(jù)包中的IO類型:有連接請(qǐng)求PerIoData.IO_TYPE := IO_TYPE_Accept;//通知工作線程,有新的套接字連接<第三個(gè)參數(shù)>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);//得到創(chuàng)建線程是傳遞過來的IOCPwhile(TRUE) dobegin//工作者線程會(huì)停止到GetQueuedCompletionStatus函數(shù)處,直到接受到數(shù)據(jù)為止lvResultStatus := GetQueuedCompletionStatus(CompletionPort,BytesTransferred,cSocket,POverlapped(PerIoData), INFINITE); if (lvResultStatus = False) thenbegin//當(dāng)客戶端連接斷開或者客戶端調(diào)用closesocket函數(shù)的時(shí)候,函數(shù)GetQueuedCompletionStatus會(huì)返回錯(cuò)誤。如果我們加入心跳后,在這里就可以來判斷套接字是否依然在連接。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 //連接請(qǐng)求beginGlobalFree(DWORD(PerIoData));end else if PerIoData.IO_TYPE = IO_TYPE_Recv thenbegin 可以在這里處理數(shù)據(jù)…… GlobalFree(DWORD(PerIoData));end;/分配內(nèi)存<可以加入內(nèi)存池>PerIoData := LPPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA)));ZeroMemory(@PerIoData.Overlapped, sizeof(OVERLAPPED));Flags := 0;/進(jìn)入投遞收取動(dòng)作PerIoData.IO_TYPE := IO_TYPE_Recv;PerIoData.DataBuf.len:=DATA_BUFSIZE;ZeroMemory(@PerIoData.Buffer,sizeof(@PerIoData.Buffer));PerIoData.DataBuf.buf := @PerIoData.Buffer;/異步收取數(shù)據(jù)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;總結(jié)
以上是生活随笔為你收集整理的Delphi-IOCP学习笔记三====工作线程和Listener的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java怎么抛出异常
- 下一篇: 华为mate20x隐藏功能