zoukankan      html  css  js  c++  java
  • delphi之IOCP学习(一)

       困扰已久的网络通信(IOCP:完成端口),今天终于揭开她的神秘面纱了,之前百度N久还是未能理解IOCP,网络上好多博文都没有贴出源码,初学者很难正在理解IOCP并自己写出通信例子 ,经过努力,今天自己终于做出了简单的测试程序,下面贴出源码,水平有限,难免有错,希望不要误人子弟。

    1、Svr主窗体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    unit Umain;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, UIOCPSvr;
     
     
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        mmoRev: TMemo;
        procedure Button1Click(Sender: TObject);
      private
        IOCPSvr: TIOCPSvr;
        { Private declarations }
      public
        { Public declarations }
     
      end;
     
    var
      Form1: TForm1;
     
     
     
    implementation
     
    {$R *.dfm}
     
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      IOCPSvr := TIOCPSvr.Create(Self);
      IOCPSvr.Host := '192.168.1.86';
      IOCPSvr.Port := 8988;
      IOCPSvr.open;
    end;
     
    end.

       2、IOCP 服务端实现代码

    复制代码
      1 unit UIOCPSvr;
      2 
      3 interface
      4 
      5 uses
      6   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      7   Dialogs, StdCtrls, JwaWinsock2;
      8 
      9 const
     10   DATA_BUFSIZE = 1024;
     11 
     12 type
     13   LPVOID = Pointer;
     14   {* 完成端口操作定义 *}
     15   TIocpOperate = (ioNone, ioCon, ioRead, ioWrite, ioStream, ioExit);
     16   PIocpRecord = ^TIocpRecord;
     17   TIocpRecord = record
     18     Overlapped: TOverlapped; //完成端口重叠结构
     19     WsaBuf: TWsaBuf; //完成端口的缓冲区定义
     20     IocpOperate: TIOCPOperate; //当前操作类型
     21   end;
     22 
     23 type
     24   TThreadRev = class(TThread)
     25   private
     26     pData: Pointer;
     27   protected
     28     procedure Execute; override;
     29   public
     30     constructor Create(CreateSuspended: Boolean; adata: Pointer);
     31     destructor Destroy; override;
     32   end;
     33 
     34 
     35   TThreadCon = class(TThread)
     36   private
     37     PSocket: TSocket;
     38     lvIOPort: THandle;
     39   protected
     40     procedure Execute; override;
     41   public
     42     constructor Create(CreateSuspended: Boolean; var aSocket: TSocket; var aIOport: THandle);
     43     destructor Destroy; override;
     44   end;
     45 
     46 
     47   TIOCPSvr = class(TComponent)
     48   private
     49     FHost: string;
     50     FPort: Integer;
     51     ThreadCon: TThreadCon;
     52     ThreadRev: TThreadRev;
     53   protected
     54   public
     55     constructor Create(AOwner: TComponent); override;
     56     destructor Destroy; override;
     57     procedure open;
     58   published
     59     property Port: Integer read FPort write FPort;
     60     property Host: string read FHost write FHost;
     61   end;
     62 
     63 
     64 procedure SendData(astr: string; FSocket: TSocket); //发生数据
     65 function PIocpAllocate(ALen: Cardinal): PIocpRecord;  //分配内存
     66 procedure PIocpRelease(var AValue: PIocpRecord); //释放内存
     67 
     68 implementation
     69 
     70 uses Umain;
     71 
     72 function PIocpAllocate(ALen: Cardinal): PIocpRecord;
     73 begin
     74   New(Result);
     75   Result.Overlapped.Internal := 0;
     76   Result.Overlapped.InternalHigh := 0;
     77   Result.Overlapped.Offset := 0;
     78   Result.Overlapped.OffsetHigh := 0;
     79   Result.Overlapped.hEvent := 0;
     80   Result.IocpOperate := ioNone;
     81   Result.WsaBuf.buf := GetMemory(ALen);
     82   Result.WsaBuf.len := ALen;
     83 end;
     84 
     85 
     86 procedure PIocpRelease(var AValue: PIocpRecord);
     87 begin
     88   FreeMemory(AValue.WsaBuf.buf);
     89   AValue.WsaBuf.buf := nil;
     90   Dispose(AValue);
     91 end;
     92  
     93 
     94 procedure SendData(astr: string; FSocket: TSocket);
     95 var
     96   IocpRec: PIocpRecord;
     97   iErrCode: Integer;
     98   dSend, dFlag: DWORD;
     99   FOutputBuf: TMemoryStream;
    100 begin
    101 
    102   FOutputBuf := TMemoryStream.Create;
    103   FOutputBuf.WriteBuffer(astr[1], Length(astr));
    104 
    105   New(IocpRec);
    106   IocpRec.Overlapped.Internal := 0;
    107   IocpRec.Overlapped.InternalHigh := 0;
    108   IocpRec.Overlapped.Offset := 0;
    109   IocpRec.Overlapped.OffsetHigh := 0;
    110   IocpRec.Overlapped.hEvent := 0;
    111   IocpRec.WsaBuf.buf := GetMemory(Length(astr));
    112   IocpRec.WsaBuf.len := Length(astr);
    113 
    114   IocpRec.IocpOperate := ioWrite;
    115   System.Move(PAnsiChar(FOutputBuf.Memory)[0], IocpRec.WsaBuf.buf^, FOutputBuf.Size);
    116   dFlag := 0;
    117   if WSASend(FSocket, @IocpRec.WsaBuf, 1, dSend, dFlag, @IocpRec.Overlapped, nil) = SOCKET_ERROR then
    118   begin
    119     iErrCode := WSAGetLastError;
    120     if iErrCode <> ERROR_IO_PENDING then
    121     begin
    122      // FIocpServer.DoError('WSASend', GetLastWsaErrorStr);
    123       //ProcessNetError(iErrCode);
    124     end;
    125   end;
    126   FreeAndNil(FOutputBuf);
    127 end;
    128 
    129 
    130 { TIOCPSvr }
    131 
    132 constructor TIOCPSvr.Create(AOwner: TComponent);
    133 begin
    134   inherited;
    135 
    136 end;
    137 
    138 destructor TIOCPSvr.Destroy;
    139 begin
    140   ThreadCon.Terminate;
    141   if ThreadCon.Suspended then
    142     ThreadCon.Resume;
    143 
    144   FreeAndNil(ThreadCon);
    145   inherited;
    146 end;
    147 
    148 procedure TIOCPSvr.open;
    149 var
    150   WSData: TWSAData;
    151   lvIOPort: THandle;
    152   lvAddr: TSockAddr;
    153   sSocket: TSocket;
    154 begin 
    155 
    156  //加载初始化SOCKET。使用的是2.2版为了后面方便加入心跳。
    157   WSAStartup($0202, WSData);
    158 
    159 // 创建一个完成端口(内核对象),新建一个IOCP
    160   lvIOPort := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
    161 
    162  //创建一个工作线程,调试用
    163   ThreadRev := TThreadRev.Create(False, Pointer(lvIOPort));
    164 
    165 //创建一个套接字,将此套接字和一个端口绑定并监听此端口。
    166   sSocket := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, WSA_FLAG_OVERLAPPED);
    167   if sSocket = SOCKET_ERROR then
    168   begin
    169     closesocket(sSocket);
    170     WSACleanup();
    171   end;
    172   lvAddr.sin_family := AF_INET;
    173   lvAddr.sin_port := htons(Port);
    174   lvAddr.sin_addr.s_addr := htonl(INADDR_ANY);
    175   if bind(sSocket, @lvAddr, sizeof(lvAddr)) = SOCKET_ERROR then
    176   begin
    177     closesocket(sSocket);
    178   end;
    179   listen(sSocket, 20);
    180 
    181   //连接线程,当有客户端请求建立连接在该现场中处理
    182   ThreadCon := TThreadCon.Create(False, sSocket, lvIOPort);
    183 
    184 //下面循环进行循环获取客户端的请求。这注释部分放到 ThreadCon线程中处理了
    185 //  while (TRUE) do
    186 //  begin
    187 //     //当客户端有连接请求的时候,WSAAccept函数会新创建一个套接字cSocket。这个套接字就是和客户端通信的时候使用的套接字。
    188 //    cSocket := WSAAccept(sSocket, nil, nil, nil, 0);
    189 //
    190 //     //判断cSocket套接字创建是否成功,如果不成功则退出。
    191 //    if (cSocket = SOCKET_ERROR) then
    192 //    begin
    193 //      closesocket(sSocket);
    194 //      exit;
    195 //    end;
    196 //
    197 //     //将套接字、完成端口绑定在一起。
    198 //    lvPerIOPort := CreateIoCompletionPort(cSocket, lvIOPort, cSocket, 0);
    199 //    if (lvPerIOPort = 0) then
    200 //    begin
    201 //      Exit;
    202 //    end;
    203 //
    204 //     //初始化数据包
    205 //    PerIoData := PIocpAllocate(DATA_BUFSIZE);
    206 //    PerIoData.IocpOperate := ioCon;
    207 //     //通知工作线程,有新的套接字连接<第三个参数>
    208 //    PostQueuedCompletionStatus(lvIOPort, 0, cSocket, POverlapped(PerIOData));
    209 //  end;
    210 
    211 end;
    212 
    213 
    214 
    215 { TThreadCon }
    216 
    217 constructor TThreadCon.Create(CreateSuspended: Boolean; var aSocket: TSocket; var aIOport: THandle);
    218 begin
    219   inherited create(CreateSuspended);
    220   PSocket := aSocket;
    221   lvIOPort := aIOport;
    222 end;
    223 
    224 destructor TThreadCon.Destroy;
    225 begin
    226 
    227   inherited;
    228 end;
    229 
    230 procedure TThreadCon.Execute;
    231 var
    232   cSocket: TSocket;
    233   lvPerIOPort: Integer;
    234   PerIoData: PIocpRecord;
    235 begin
    236   inherited;
    237   while not Terminated do
    238   begin
    239 
    240      //当客户端有连接请求的时候,WSAAccept函数会新创建一个套接字cSocket。这个套接字就是和客户端通信的时候使用的套接字。
    241     cSocket := WSAAccept(PSocket, nil, nil, nil, 0);
    242 
    243      //判断cSocket套接字创建是否成功,如果不成功则退出。
    244     if (cSocket = SOCKET_ERROR) then
    245     begin
    246       closesocket(PSocket);
    247       exit;
    248     end;
    249 
    250      //将套接字、完成端口绑定在一起。
    251     lvPerIOPort := CreateIoCompletionPort(cSocket, lvIOPort, cSocket, 0);
    252     if (lvPerIOPort = 0) then
    253     begin
    254       Exit;
    255     end;
    256 
    257      //初始化数据包
    258     PerIoData := PIocpAllocate(DATA_BUFSIZE);
    259     PerIoData.IocpOperate := ioCon;
    260      //通知工作线程,有新的套接字连接<第三个参数>
    261     PostQueuedCompletionStatus(lvIOPort, 0, cSocket, POverlapped(PerIOData)); 
    262   end;
    263 
    264 end;
    265 
    266 { TThreadRev }
    267 
    268 constructor TThreadRev.Create(CreateSuspended: Boolean; adata: Pointer);
    269 begin
    270   inherited Create(CreateSuspended);
    271   pData := adata;
    272 end;
    273 
    274 destructor TThreadRev.Destroy;
    275 begin
    276 
    277   inherited;
    278 end;
    279 
    280 procedure TThreadRev.Execute;
    281 var
    282   CompletionPort: THANDLE;
    283   BytesTransferred: Cardinal;
    284   PerIoData: PIocpRecord;
    285   cSocket: TSocket;
    286   Flags: Cardinal;
    287   lvResultStatus: BOOL;
    288   temp: string;
    289 begin
    290   inherited;
    291   CompletionPort := THandle(pData);
    292 
    293   //得到创建线程是传递过来的IOCP
    294   while not Terminated do
    295   begin
    296     //工作者线程会停止到GetQueuedCompletionStatus函数处,直到接受到数据为止
    297     lvResultStatus := GetQueuedCompletionStatus(CompletionPort, BytesTransferred, cSocket, POverlapped(PerIoData), INFINITE);
    298 
    299        {//CompletionPort:新建IOCP CreateIoCompletionPort()函数返回的端口    // BytesTransferred 收到数据的长度
    300        // cSocket 个人理解就是通信sock句柄   //PerIoData 数据结构
    301       //INFINITE 超时时间,这里是一直等待的意思,GetQueuedCompletionStatus 可以参考百度百科}
    302 
    303     if (lvResultStatus = False) then
    304     begin
    305      //当客户端连接断开或者客户端调用closesocket函数的时候,函数GetQueuedCompletionStatus会返回错误。如果我们加入心跳后,在这里就可以来判断套接字是否依然在连接。
    306       if cSocket <> 0 then
    307       begin
    308         closesocket(cSocket);
    309       end;
    310       if PerIoData <> nil then
    311       begin
    312         PIocpRelease(PerIoData);
    313       end;
    314       continue;
    315     end;
    316 
    317     if PerIoData = nil then
    318     begin
    319       closesocket(cSocket);
    320       Break;
    321     end
    322     else if (PerIoData <> nil) then
    323     begin
    324 
    325       if PerIoData.IocpOperate = ioCon then //连接请求
    326       begin
    327 
    328         PIocpRelease(PerIoData);
    329       end
    330       else if PerIoData.IocpOperate = ioRead then
    331       begin
    332             ////可以在这里处理数据……
    333          temp:= Copy(string(PerIoData.WsaBuf.buf),1,BytesTransferred); //获取接收到的数据 这里只处理了字符串
    334          Form1.mmoRev.Lines.Add(format('收到客户端:%d 消息:%s',[cSocket,temp]));
    335          // temp := 'hello world !' + #13#10;  //indy TCP 需要#13#10 才能收到信息
    336         SendData(temp, cSocket); //接受什么数据原样返回
    337         PIocpRelease(PerIoData);//释放内存
    338       end;
    339       Flags := 0;
    340       /////进入投递收取动作
    341       PerIoData := PIocpAllocate(DATA_BUFSIZE);
    342       PerIoData.IocpOperate := ioRead;
    343 
    344       /////异步收取数据
    345       WSARecv(cSocket, @PerIoData.WsaBuf, 1, PerIoData.WsaBuf.len, Flags, @PerIoData.Overlapped, nil);
    346       if (WSAGetLastError() <> ERROR_IO_PENDING) then
    347       begin
    348         closesocket(cSocket);
    349         if PerIoData <> nil then
    350         begin
    351           PIocpRelease(PerIoData);
    352         end;
    353         Continue;
    354       end;
    355     end;
    356   end;
    357 
    358 end;
    359 
    360 end.
    复制代码

    3、indy TCP 客户端

    复制代码
     1 unit Unit1;
     2 
     3 interface
     4 
     5 uses
     6   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     7   Dialogs, IdTCPServer, IdBaseComponent, IdComponent, IdTCPConnection,
     8   IdTCPClient, StdCtrls, Sockets;
     9 
    10 type
    11   TForm1 = class(TForm)
    12     IdTCPClient1: TIdTCPClient;
    13     btnCon: TButton;
    14     mmo1: TMemo;
    15     btnSend: TButton;
    16     btnRev: TButton;
    17     edtSend: TEdit;
    18     edtHost: TEdit;
    19     edtPort: TEdit;
    20     procedure IdTCPClient1Connected(Sender: TObject);
    21     procedure btnConClick(Sender: TObject);
    22     procedure btnSendClick(Sender: TObject);
    23     procedure btnRevClick(Sender: TObject);
    24   private
    25     { Private declarations }
    26   public
    27     { Public declarations }
    28   end;
    29 
    30 var
    31   Form1: TForm1;
    32 
    33 implementation
    34 
    35 {$R *.dfm}
    36 
    37 procedure TForm1.IdTCPClient1Connected(Sender: TObject);
    38 begin
    39    mmo1.Lines.Add('用户连接上');
    40 end;
    41 
    42 procedure TForm1.btnConClick(Sender: TObject);
    43 begin
    44 
    45  IdTCPClient1.Host:=edtHost.Text;
    46  IdTCPClient1.Port:=StrToInt(edtPort.Text) ;
    47  IdTCPClient1.Connect();
    48  btnCon.Enabled:=False;
    49  btnSend.Enabled:=True;
    50 end;
    51 
    52 procedure TForm1.btnSendClick(Sender: TObject);
    53 begin
    54   IdTCPClient1.WriteLn(edtSend.Text);
    55   btnSend.Enabled:=False;
    56   btnRev.Enabled:=True;
    57 end;
    58 
    59 procedure TForm1.btnRevClick(Sender: TObject);
    60 begin
    61       mmo1.Lines.Add( IdTCPClient1.ReadLn(#13#10,-1,-1));
    62       btnRev.Enabled:=False;
    63       btnSend.Enabled:=True;
    64 end;
    65 
    66 end.
    
    
    Q群   Delphi Home  235236282,欢迎delphi 爱好者加入,一起学习、进步。
    
    
    
    
    

    http://blog.csdn.net/u013051638/article/details/53336762

  • 相关阅读:
    Hive-拉链表
    JAVA-POI
    GreenPlum-数据存储目录迁移及常用操作
    CDH6 高版本hbase+solr实现二级索引
    GreenPlum执行gpfdist报错:libssl.so.1.0.0: cannot open shared object file: No such file or directory
    rasdaman介绍及安装
    博学谷-数据分析pandas
    博学谷-数据分析numpy
    博学谷-数据分析matplotlib
    python基础学习笔记
  • 原文地址:https://www.cnblogs.com/findumars/p/8196087.html
Copyright © 2011-2022 走看看