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数据结构和单句柄数据结构的定义等等。

posted @ 2012-09-20 08:56  马儿快跑  阅读(571)  评论(0编辑  收藏  举报