• DELPHI中完成端口(IOCP)的简单分析(1)


    DELPHI中完成端口(IOCP)的简单分析(1)

     
    用DELPHI开发网络代码已经有一段时间了!
    我发现在网上用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;
    创建IOCP的工作已经完成,下一次我将写IOCP的工作者线程的处理方法,谢谢!
  • 相关阅读:
    springboot maven打包插件
    maven打包指定main入口插件
    团队开发环境一致性性要求
    springboot 在idea中实现热部署
    IDEA 2018.1可用License服务(持续更新)
    IDEA打jar包
    3月18号
    3月17号
    3月16号
    3月13号
  • 原文地址:https://www.cnblogs.com/bwdblogs/p/10504143.html
Copyright © 2020-2023  润新知