zoukankan      html  css  js  c++  java
  • WinSock学习笔记6:IOCP完成端口模型

    WinSock学习笔记6:IOCP完成端口模型

    unit Unit1;

    interface

    uses
      WinSock2, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls;

    type
      
    //单IO数据结构
      LPER_IO_OPERATION_DATA 
    = ^TPER_IO_OPERATION_DATA;
      TPER_IO_OPERATION_DATA 
    = packed record
        Overlapped: WSAOverlapped;
        DataBuf: WSABuf;
        Buff: 
    array [0..10of Char;
        BytesSend: DWORD;
        BytesRecv: DWORD;
      
    end;

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

      TListenThread 
    = class(TThread)
      
    private
      
    protected
        
    procedure Execute;override;
      
    public
        
    constructor Create;
      
    end;

      TForm1 
    = class(TForm)
        Memo1: TMemo;
        Button1: TButton;
        
    procedure FormCreate(Sender: TObject);
      
    private
        
    { Private declarations }
      
    public
        
    { Public declarations }
      
    end;

    var
      Form1: TForm1;
      ServerSocket: TSocket;

    implementation

    {$R *.dfm}

    //工作者线程
    function WorkThread(CompletionPortID: Pointer):DWORD; stdcall;
    var
      CompletionPort: THandle;
      BytesTransferred: DWORD;
      PerHandleData: LPER_HANDLE_DATA;
      PerIOData: LPER_IO_OPERATION_DATA;
      Flags: DWORD;
      RecvBytes: DWORD;
    begin
      CompletionPort:
    = THandle(CompletionPortID);

      
    while True do
      
    begin
        
    //获取完成端口上的队列的完成状态
        GetQueuedCompletionStatus(CompletionPort, BytesTransferred, DWORD(PerHandleData),
          POverlapped(PerIOData), INFINITE);

        
    //判断是客户端发来的数据还是服务端发出的数据
        
    if PerIOData.BytesRecv = 0 then
        
    begin
          PerIOData.BytesRecv:
    = BytesTransferred;
          PerIOData.BytesSend:
    = 0;
        
    end else
          PerIOData.BytesSend:
    = PerIOData.BytesSend + BytesTransferred;

        
    if PerIOData.BytesRecv > PerIOData.BytesSend then
        
    begin
          ZeroMemory(@(PerIOData.Overlapped), SizeOf(WSAOverlapped));
          PerIOData.DataBuf.buf:
    = PerIOData.Buff + PerIOData.BytesSend;
          PerIOData.DataBuf.len:
    = PerIOData.BytesRecv - PerIOData.BytesSend;

          
    //显示收到的数据,这样做是不安全的,示例而已 :)
          Form1.Memo1.Lines.Add(PerIOData.Buff);
        
    end;

        
    //重置数据
        PerIOData.BytesRecv:
    = 0;
        PerIOData.DataBuf.len:
    = 22;
        PerIOData.DataBuf.buf:
    = @PerIOData.Buff;

        
    //再次投递
        WSARecv(PerHandleData.Socket, @(PerIOData.DataBuf), 
    1, RecvBytes, Flags,
          @(PerIOData.Overlapped), 
    nil);
      
    end;
    end;

    { TWorkThread }

    constructor TListenThread.Create;
    begin
      
    inherited Create(False);
      FreeOnTerminate:
    = True;
    end;

    procedure TListenThread.Execute;
    var
      WSData: TWSAData;
      CompletionPort: THandle;
      lpSystemInfo: TSystemInfo;
      Idx: Integer;
      ThreadID: DWORD;
      LocalAddr: TSockaddr;
      ClientAddr: TSockaddr;
      ClientSocket: TSocket;

      PER_HANDLE_DATA: LPER_HANDLE_DATA;
      PER_IO_OPERATION_DATA: LPER_IO_OPERATION_DATA;

      RecvBytes: DWORD;
      Flags: DWORD;
    begin
      
    inherited;

      
    //初始化Winsock
      WSAStartUp($
    202, WSData);
      
    //创建完成端口
      CompletionPort:
    = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 000);
      
    //根据处理器数量创建工作者线程的数量(网上流传的说法是线程数量=cpu数量*2+2)
      GetSystemInfo(lpSystemInfo);
      
    for Idx := 1 to lpSystemInfo.dwNumberOfProcessors *2 + 2 do
        
    //创建工作者线程,并将完成端口句柄传递给线程
        CreateThread(
    nil0, @WorkThread, Pointer(CompletionPort), 0, ThreadID);
      
    //创建监听套接字
      ServerSocket:
    = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nil0, WSA_FLAG_OVERLAPPED);
      
    //设置LocalAddr的参数
      LocalAddr.sin_family:
    = AF_INET;   //IPV4族
      LocalAddr.sin_addr.S_addr:
    = INADDR_ANY;//这里不能写Inet_addr('127.0.0.1'),否则会绑定失败,不清楚原因是什么;
      LocalAddr.sin_port:
    = Htons(1077); //Host To Net Short,主机字节顺序转为网络字节顺序
      
    //绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数
      Bind(ServerSocket, @LocalAddr, SizeOf(LocalAddr));
      
    //开始监听
      Listen(ServerSocket, 
    5);

      
    while not Terminated do
      
    begin
        ClientSocket:
    = WSAAccept(ServerSocket, ClientAddr, nilnil0);
        
    //创建TPER_HANDLE_DATA结构的变量保存客户端Socket
        PER_HANDLE_DATA:
    = LPER_HANDLE_DATA(GlobalAlloc(GPTR, SizeOf(TPER_HANDLE_DATA)));
        PER_HANDLE_DATA.Socket:
    = ClientSocket;
        
    //把完成端口和客户端套接字关联起来
        CreateIOCompletionPort(ClientSocket, CompletionPort, DWORD(PER_HANDLE_DATA), 
    0);
        
    //创建TPER_IO_OPERATION_DATA结构的变量,关联WSARecv函数
        PER_IO_OPERATION_DATA:
    = LPER_IO_OPERATION_DATA(GlobalAlloc(GPTR, SizeOf(TPER_IO_OPERATION_DATA)));
        ZeroMemory(@PER_IO_OPERATION_DATA.Overlapped, SizeOf(WSAOverlapped));
        PER_IO_OPERATION_DATA.BytesSend:
    = 0;
        PER_IO_OPERATION_DATA.BytesRecv:
    = 0;
        PER_IO_OPERATION_DATA.DataBuf.len:
    = 22;
        PER_IO_OPERATION_DATA.DataBuf.buf:
    = @PER_IO_OPERATION_DATA.Buff;

        WSARecv(ClientSocket, @(PER_IO_OPERATION_DATA.DataBuf), 
    1, RecvBytes,
          Flags, @(PER_IO_OPERATION_DATA.Overlapped), 
    nil);
      
    end;
    end;


    procedure TForm1.FormCreate(Sender: TObject);
    begin
      
    //创建监听线程
      TListenThread.Create();
    end;


    end.

    该代码仅仅是练习IOCP的函数使用和大体的实现步骤,里面很多细节并不是唯一的方法,例如单IO数据结构和单句柄数据结构的定义等等。

  • 相关阅读:
    Python并行编程(十三):进程池和mpi4py模块
    Python 列表集合 字典推导式、生成器表达式
    Python 迭代对象、迭代器
    Python 参数,嵌套函数 的变量 使用
    Python 编码进阶
    Python 深浅Copy
    Python 代码块、缓存机制
    Python 列表,字典 相关方法
    初识 python 字符串 相关函数
    初识编码格式
  • 原文地址:https://www.cnblogs.com/tc310/p/1837943.html
Copyright © 2011-2022 走看看