完成端口(Iocp)。

// 单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;

  
// 单句柄数据结构
  LPPER_HANDLE_DATA  =   ^ PER_HANDLE_DATA;
  PER_HANDLE_DATA 
=  packed record
    Socket: TSocket;
  end;

/// 代码测试用过 ,一直没有机会在实际项目中应用,遗憾!~~~~~~~~~


procedure TServerClientThread.Execute;
var
  i ,Flags: Integer;
  sto  : sockaddr_in;
begin
  
// 加载SOCKET。
   if  WSAStartUp($ 202 , wsData)  <>   0  then
  begin
    WSACleanup();
  end;
  
// 创建一个完成端口。
  CompletionPort: = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0 , 0 , 0 );
  
// 根据CPU的数量创建CPU*2数量的工作者线程。
  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;
  
// 创建一个套接字,将此套接字和一个端口绑定并监听此端口
  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( 18715 );
  sto.sin_addr.s_addr:
= htonl(INADDR_ANY);   //     inet_addr('10.0.0.187');
  
// 将此套接字和一个端口绑定并监听此端口
   if  bind(Listensc,sto, sizeof (sto)) = SOCKET_ERROR then
  begin
    closesocket(Listensc);
  end;
  listen(Listensc,
20 );
  
while  (TRUE)  do
  begin
    
// 当客户端有连接请求的时候,WSAAccept函数会新创建一个套接字Acceptsc。
    
// 这个套接字就是和客户端通信的时候使用的套接字。
    Acceptsc: =  WSAAccept(Listensc, nil, nil, nil,  0 );
    
// 判断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;
     
// 。将套接字、完成端口和“单句柄数据结构”三者绑定在一起。
     if  (CreateIoCompletionPort(Acceptsc, CompletionPort, DWORD(PerHandleData),  0 =   0 ) then
    begin
      exit;
    end;
    
//  创建一个“单IO数据结构”其中将PerIoData.BytesSEND 和PerIoData.BytesRECV 均设置成0。
    
// 说明此“单IO数据结构”是用来接受的。
    ZeroMemory(@PerIoData.Overlapped,  sizeof (OVERLAPPED));
    PerIoData.BytesSEND :
=   0 ;
    PerIoData.BytesRECV :
=   0 ;
    PerIoData.DataBuf.len :
=   1024 ;
    
//  ????????????????
    PerIoData.DataBuf.buf : =  @PerIoData.Buffer;
    Flags :
=   0 ;
    
//   用此“单IO数据结构”来接受Acceptsc套接字的数据。
     if  (WSARecv(Acceptsc, @(PerIoData.DataBuf),  1 , @RecvBytes, @Flags,@(PerIoData.Overlapped), nil)  =  SOCKET_ERROR) then
     begin
        
if  (WSAGetLastError()  <>  ERROR_IO_PENDING) then
        begin
           exit;
        end
     end;
  end;
end;



//  工作者线程
function ServerWorkerThread(CompletionPortID:Pointer):Integer;stdcall;
var
  CompletionPort: THANDLE;
  BytesTransferred: DWORD ;
 
//   Overlapped: POVERLAPPED;
  PerHandleData: LPPER_HANDLE_DATA ;
  PerIoData: LPPER_IO_OPERATION_DATA ;
  SendBytes, RecvBytes: DWORD;
  Flags: DWORD;
  TempSc : TSocket;
begin
  CompletionPort:
= THANDLE(CompletionPortID);
  
// 得到创建线程是传递过来的IOCP
   while (TRUE)  do
  begin
  
// 工作者线程会停止到GetQueuedCompletionStatus函数处,直到接受到数据为止       
  
//   PerIoData
   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
       
// Client断开事件  ???

        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 你可以对数据进行相关的处理了。
      
// 产生一个事件?????????
      
// FOnClientRead
      
// if Assigned(FOnClientRead) then
      
// FOnClientRead(TempSc,PerIoData.DataBuf.buf,PerIoData.DataBuf.len);
      
// 当我们将数据处理完毕以后,应该将此套接字设置为结束状态,同时初始化和它绑定在一起的数据结构。
      ZeroMemory(@(PerIoData.Overlapped),  sizeof (OVERLAPPED));
      PerIoData.BytesRECV :
=   0 ;
      Flags :
=   0 ;
      ZeroMemory(@(PerIoData.Overlapped), 
sizeof (OVERLAPPED));
      PerIoData.DataBuf.len :
=   0 //  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;
/////
// 1年前写的代码,根据网上一位强哥写的blog  根据自己的理解,琢磨出来的。
// 只是实现了的功能,没有在仔细调试过,也没有实际环境中经过压力测试。
//  如果哪位大哥要用到iocp请联系我。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值