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

我發現在網上用VC來實現完成端口(IOCP)的代碼很多,但是使用DELPHI來實現的就比較少了。對IOCP講的清楚的就更少了。在這裏我把自己編寫DELPHI下的IOCP寫出來,希望對剛學完成端口的朋友有個幫助。
首先我們來了解一些在使用IOCP的時候需要使用的一些結構!
(1):單IO數據結構
  LPVOID = Pointer;
  LPPER_IO_OPERATION_DATA = ^ PER_IO_OPERATION_DATA ;
  PER_IO_OPERATION_DATA = packed record
    Overlapped: 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;用來標誌接受數據的長度。因爲完成端口的工作者線程可以接受到來自客戶端的數據,同時還可以接受到自己發送給客戶端的數據,所以我們使用BytesSEND,BytesRECV變量來說是用來區分這次的數據是來自客戶端的數據還是自己發送出去的數據。詳細的使用方法,我會在下面詳細說明。
(2):“單句柄數據結構”
  LPPER_HANDLE_DATA = ^ PER_HANDLE_DATA;
  PER_HANDLE_DATA = packed record
    Socket: TSocket;
  end;
下來我從編寫一個完成端口的爲例說明。
if WSAStartUp($202, wsData) <> 0 then
begin
   WSACleanup();
end;
加載SOCKET。我使用的是2.2版爲了後面方便加入心跳。
CompletionPort:=CreateIOCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
創建一個完成端口。
GetSystemInfo(LocalSI);
for I:=0 to LocalSI.dwNumberOfProcessors * 2 -1 do
begin
   hThread := CreateThread(nil, 0, @ServerWorkerThread, Pointer(CompletionPort),0, ThreadID);
   if (hThread = 0) then
   begin
       Exit;
   end;
   CloseHandle(hThread);
end;
根據CPU的數量創建CPU*2數量的工作者線程。
Listensc:=WSASocket(AF_INET,SOCK_STREAM,0,Nil,0,WSA_FLAG_OVERLAPPED);
if Listensc=SOCKET_ERROR then
begin
    closesocket(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))=SOCKET_ERROR then
begin
   closesocket(Listensc);
end;
listen(Listensc,20);
創建一個套接字,將此套接字和一個端口綁定並監聽此端口。
while (TRUE) do
begin
   Acceptsc:= WSAAccept(Listensc, nil, nil, nil, 0);
   當客戶端有連接請求的時候,WSAAccept函數會新創建一個套接字Acceptsc。這個套接字就是和客戶端通信的時候使用的套接字。
   if (Acceptsc= SOCKET_ERROR) then
   begin
      closesocket(Listensc);
      exit;
   end;
   判斷Acceptsc套接字創建是否成功,如果不成功則退出。
   PerHandleData := LPPER_HANDLE_DATA (GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA)));
   if (PerHandleData = nil) then
   begin
      exit;
   end;
   PerHandleData.Socket := Acceptsc;
   創建一個“單句柄數據結構”將Acceptsc套接字綁定。
   if (CreateIoCompletionPort(Acceptsc, CompletionPort, DWORD(PerHandleData), 0) = 0) then
   begin
      exit;
   end;
   將套接字、完成端口和“單句柄數據結構”三者綁定在一起。
   PerIoData := LPPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA)));
   if (PerIoData = nil) then
   begin
      exit;
   end;
   ZeroMemory(@PerIoData.Overlapped, sizeof(OVERLAPPED));
   PerIoData.BytesSEND := 0;
   PerIoData.BytesRECV := 0;
   PerIoData.DataBuf.len := 1024;
   PerIoData.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) then
   begin
      if (WSAGetLastError() <> ERROR_IO_PENDING) then
      begin
         //最近在檢查代碼的時候發現以前這裏只是使用Exit來退出是不正確的。這裏需要刪除申請的單IO數據結構,否子會出現內存泄露。 (2008年3月24日)
         //Exit;
        closesocket(AcceptSc);
        if PerIoData <> nil then
        begin
          GlobalFree(DWORD(PerIoData));
        end;
        Continue;
      end
   end;
   用此“單IO數據結構”來接受Acceptsc套接字的數據。
end;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章