zoukankan      html  css  js  c++  java
  • 最精简的IOCP封装

    最精简的IOCP封装,DELPHI XE8直接编译通过。Winsock2.pas即使用DELPHI自带的,相信XE7也能编译,或者XE6,XE5也能。

    单说Winsock2.pas,我见过无数种版本的了,各版本WINSOCK 2的API的方法的参数的数据类型居然都有出入,使用不同人封装的Winsock2.pas源码都要进行相应的调整,

    否则无法编译通过,我认为还是使用DELPHI官方的最为靠谱。

    要用于实际应用的话,还要进行“粘包处理”。

    我在DELPHI XE8下测试OK。

    unit Unit1;

    interface

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

    const
    BUF_SIZE=1024;

    type
    //单IO数据结构
    LPER_IO_DATA = ^TPER_IO_DATA;
    TPER_IO_DATA = packed record
    Overlapped: WSAOverlapped;
    DataBuf: WSABuf;
    Buf: array [0..BUF_SIZE-1] of AnsiChar;
    SendBytes: DWORD;
    RecvBytes: 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;
    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_DATA;
    Flags: DWORD;
    RecvBytes: DWORD;
    begin
    CompletionPort:= THandle(CompletionPortID);

    while True do
    begin
    //获取完成端口上的队列的完成状态
    GetQueuedCompletionStatus(CompletionPort, BytesTransferred, ULONG_PTR(PerHandleData), POverlapped(PerIOData), INFINITE);
    //判断是客户端发来的数据还是服务端发出的数据
    if PerIOData.RecvBytes = 0 then
    begin
    PerIOData.RecvBytes:= BytesTransferred;
    PerIOData.SendBytes:= 0;
    end else
    PerIOData.SendBytes:= PerIOData.SendBytes + BytesTransferred;

    if PerIOData.RecvBytes > PerIOData.SendBytes then
    begin
    ZeroMemory(@(PerIOData.Overlapped), SizeOf(WSAOverlapped));
    PerIOData.DataBuf.buf:= PerIOData.Buf + PerIOData.SendBytes;
    PerIOData.DataBuf.len:= PerIOData.RecvBytes - PerIOData.SendBytes;

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

    //重置数据
    PerIOData.RecvBytes:= 0;
    PerIOData.DataBuf.len:= BUF_SIZE;
    PerIOData.DataBuf.buf:= @PerIOData.Buf;

    //再次投递
    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;
    SI: TSystemInfo;
    Idx: Integer;
    ThreadID: DWORD;
    LocalAddr:sockaddr_in;
    ClientAddr: sockaddr;
    ClientSocket: TSocket;

    PER_HANDLE_DATA: LPER_HANDLE_DATA;
    PER_IO_DATA: LPER_IO_DATA;

    RecvBytes: DWORD;
    Flags: DWORD;
    begin

    //初始化Winsock
    WSAStartUp($202, WSData);
    //创建完成端口
    CompletionPort:= CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
    //根据处理器数量创建工作者线程的数量
    GetSystemInfo(SI);
    for Idx := 1 to SI.dwNumberOfProcessors do
    //创建工作者线程,并将完成端口句柄传递给线程
    CreateThread(nil, 0, @WorkThread, Pointer(CompletionPort), 0, ThreadID);
    //创建监听套接字
    ServerSocket:= WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nil, 0, 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(8000); //Host To Net Short,主机字节顺序转为网络字节顺序
    //绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数
    Bind(ServerSocket, sockaddr(LocalAddr), SizeOf(LocalAddr));
    //开始监听
    Listen(ServerSocket, 5);

    while not Terminated do
    begin
    ClientSocket:= WSAAccept(ServerSocket,@ClientAddr, nil, nil, 0);
    //创建TPER_HANDLE_DATA结构的变量保存客户端Socket
    PER_HANDLE_DATA:= LPER_HANDLE_DATA(GlobalAlloc(GPTR, SizeOf(TPER_HANDLE_DATA)));
    PER_HANDLE_DATA.Socket:= ClientSocket;
    //把完成端口和客户端套接字关联起来
    CreateIOCompletionPort(ClientSocket, CompletionPort, ulong_ptr(PER_HANDLE_DATA), 0);
    //创建TPER_IO_DATA结构的变量,关联WSARecv函数
    PER_IO_DATA:= LPER_IO_DATA(GlobalAlloc(GPTR, SizeOf(TPER_IO_DATA)));
    ZeroMemory(@PER_IO_DATA.Overlapped, SizeOf(WSAOverlapped));
    PER_IO_DATA.SendBytes:= 0;
    PER_IO_DATA.RecvBytes:= 0;
    PER_IO_DATA.DataBuf.len:= BUF_SIZE;
    PER_IO_DATA.DataBuf.buf:= @PER_IO_DATA.Buf;

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


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


    end.

  • 相关阅读:
    团队项目-第一阶段冲刺7
    团队项目-第一阶段冲刺6
    Spring Boot 揭秘与实战(七) 实用技术篇
    Spring Boot 揭秘与实战(七) 实用技术篇
    Spring Boot 揭秘与实战(六) 消息队列篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(四) 配置文件篇
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/4672039.html
Copyright © 2011-2022 走看看