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.

  • 相关阅读:
    排序算法的实现(冒泡,选择,插入 O(N*N)--理解方法实现
    HTTPS工作原理和TCP握手机制
    HTTP协议学习
    IP头,TCP头,UDP头,MAC帧头定义
    单链表的实现
    数字图像处理------中值滤波
    对于矩阵的理解-- by 孟岩老师
    java编码问题总结
    jsp数据库连接大全和数据库操作封装到Javabean
    构建一个高可扩展性javabean和jsp连接数据库操作
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/4672039.html
Copyright © 2011-2022 走看看