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

 

今天我寫一下關於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協議中有數據分片的概念,這個以後我會將我處理這個的代碼分享給大家。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章