1. 程式人生 > >DELPHI中完成端口(IOCP)的簡單分析(2)

DELPHI中完成端口(IOCP)的簡單分析(2)

ast .data 我們 ESS 我會 清空 意見 lse tla

今天我寫一下關於DELPHI編寫完成端口(IOCP)的工作者線程中的東西。希望各位能提出批評意見。
上次我寫了關於常見IOCP的代碼,對於IOCP來說,接受到客戶端發送過來和自己發送出去的數據都是從工作者線程中得到。代碼和解釋如下:
function ServerWorkerThread(CompletionPortID:Pointer):Integer;stdcall;
begin
CompletionPort:=THANDLE(CompletionPortID);
//得到創建線程是傳遞過來的IOCP
while(TRUE) do
begin
//工作者線程會停止到GetQueuedCompletionStatus函數處,直到接受到數據為止
if (GetQueuedCompletionStatus(CompletionPort, BytesTransferred,DWORD(PerHandleData), POverlapped(PerIoData), INFINITE) = False) then
begin
//當客戶端連接斷開或者客戶端調用closesocket函數的時候,函數GetQueuedCompletionStatus會返回錯誤。如果我們加入心跳後,在這裏就可以來判斷套接字是否依然在連接。
if PerHandleData<>nil then
begin
closesocket(PerHandleData.Socket);
GlobalFree(DWORD(PerHandleData));
end;
if PerIoData<>nil then
begin
GlobalFree(DWORD(PerIoData));
end;
continue;
end;
if (BytesTransferred = 0) then
begin
//當客戶端調用shutdown函數來從容斷開的時候,我們可以在這裏進行處理。
if PerHandleData<>nil then
begin
TempSc:=PerHandleData.Socket;
shutdown(PerHandleData.Socket,1);
closesocket(PerHandleData.Socket);
GlobalFree(DWORD(PerHandleData));
end;
if PerIoData<>nil then
begin
GlobalFree(DWORD(PerIoData));
end;
continue;
end;
//在上一篇中我們說到IOCP可以接受來自客戶端的數據和自己發送出去的數據,兩種數據的區別在於我們定義的結構成員BytesRECV和BytesSEND的值。所以下面我們來判斷數據的來自方向。因為我們發送出去數據的時候我們設置了結構成員BytesSEND。所以如果BytesRECV=0同時BytesSEND=0那麽此數據就是我們接受到的客戶端數據。(這種區分方法不是唯一的,個人可以有自己的定義方法。只要可以區分開數據來源就可以。)
if (PerIoData.BytesRECV = 0) and (PerIoData.BytesSEND = 0) then
begin
PerIoData.BytesRECV := BytesTransferred;
PerIoData.BytesSEND := 0;
end
else
begin
PerIoData.BytesSEND := BytesTransferred;
PerIoData.BytesRECV := 0;
end;
//當是接受來自客戶端的數據是,我們進行數據的處理。
if (PerIoData.BytesRECV > PerIoData.BytesSEND) then
begin
PerIoData.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));
PerIoData.DataBuf.len := DATA_BUFSIZE;
ZeroMemory(@PerIoData.Buffer,sizeof(@PerIoData.Buffer));
PerIoData.DataBuf.buf := @PerIoData.Buffer;
if (WSARecv(PerHandleData.Socket, @(PerIoData.DataBuf), 1, @RecvBytes, @Flags,@(PerIoData.Overlapped), nil) = SOCKET_ERROR) then
begin
if (WSAGetLastError() <> ERROR_IO_PENDING) then
begin
if PerHandleData<>nil then
begin
TempSc:=PerHandleData.Socket;
closesocket(PerHandleData.Socket);
GlobalFree(DWORD(PerHandleData));
end;
if PerIoData<>nil then
begin
GlobalFree(DWORD(PerIoData));
end;
continue;
end;
end;
end
//當我們判斷出來接受的數據是我們發送出去的數據的時候,在這裏我們清空我們申請的內存空間
else
begin
GlobalFree(DWORD(PerIoData));
end;
end; end;
到此,工作者線程已經處理完成。接受數據已經沒有問題了。下一篇中我將會寫出,如何時候IOCP來發送數據代碼。今天的代碼中應該對PerIoData.BytesRECV > PerIoData.BytesSEND單另解說一下。其實如果按照上面的例子去編寫工作者線程,會覺得編寫出來的代碼會很不穩定,接受到的數據總是有錯位的現象。原因是TCP協議中有數據分片的概念,這個以後我會將我處理這個的代碼分享給大家。

DELPHI中完成端口(IOCP)的簡單分析(2)