zoukankan      html  css  js  c++  java
  • DELPHI中完成端口(IOCP)的简单分析(2)

    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协议中有数据分片的概念,这个以后我会将我处理这个的代码分享给大家。
  • 相关阅读:
    数据库如何部署上线阅读总结
    Nginx解决防盗链,服务器宕机,跨域,防DDOS
    跨域和表单重复提交
    Socet
    Redis发布订阅
    MySQL和Oracle的区别
    Redis事务、持久化、发布订阅
    Redis主从复制和哨兵模式
    Idea中使用Redis的Java客户端和Jedis
    Redis介绍及命令
  • 原文地址:https://www.cnblogs.com/bwdblogs/p/10504146.html
Copyright © 2011-2022 走看看