zoukankan      html  css  js  c++  java
  • 完成端口iocp——在螺丝壳里做道场

    WINDOWS 2000以后的操作系统才支持IOCP。WINSOCK2.0才支持IOCP。

    首先要有一个WINSOCK2.PAS的WINSOCK2.0接口调用声明单元。

    WINSOCK的版本号: WINSOCK_VERSION = $0202;

    动态库:ws2_32 = 'ws2_32.dll';

    1)服务端首先要创建一个监听SOCKET,用于监听客户端连接。

    1.1)加载WINSOCK2协议

    if WSAStartup($0202, WsaData) <> 0 then
    raise ESocketError.Create(GetLastWsaErrorStr);

    1.2)创建服务端监听SOCKET

    VAR FSocket: TSocket;

    FSocket := WSASocket(PF_INET, SOCK_STREAM, 0, nil, 0, WSA_FLAG_OVERLAPPED);
    if FSocket = INVALID_SOCKET then
    raise ESocketError.Create(GetLastWsaErrorStr);

    1.3)为创建的服务端监听SOCKET准备IP地址,端口号。。。

    var Addr: TSockAddr;

    FillChar(Addr, SizeOf(Addr), 0);
    Addr.sin_family := AF_INET;
    Addr.sin_port := htons(FPort);
    Addr.sin_addr.S_addr := htonl(INADDR_ANY); //在任何地址上监听,如果有多块网卡,会每块都监听

    1.4)为服务端监听SOCKET绑定

    if bind(FSocket, @Addr, SizeOf(Addr)) <> 0 then
    raise ESocketError.Create(GetLastWsaErrorStr);

    1.5)在服务端监听SOCKET上开启监听

    if listen(FSocket, MaxInt) <> 0 then
    raise ESocketError.Create(GetLastWsaErrorStr);

    1.6)服务端监听线程。当然也可以在主线程执行一个WHILE循环不停地接收客户端的连接,但使用监听线程显然更好。

    var
    ClientSocket: TSocket;

    ClientSocket := WSAAccept(FSocket, nil, nil, nil, 0);  // FSocket就是前面已经创建好的服务端监听SOCKET

    if ClientSocket <> INVALID_SOCKET then begin

    PostQueuedCompletionStatus(FIocpHandle, 0, ClientSocket, nil); // 客户端连接被提交到完成端口队列中

    end;

    2)IOCP

    2.1)创建完成端口。

    var FIocpHandle: THandle;

    FIocpHandle := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
    if FIocpHandle = 0 then
    raise ESocketError.Create(GetLastErrorStr);

    2.1)将客户端连接和IOCP绑定

    var cSocket: PClientSocket;

    CreateIoCompletionPort(ClientSocket, FIOCPHandle, DWORD(cSocket), 0);

     2.2)接收客户端数据

    type
    {* 完成端口操作定义 *}
    TIocpOperate = (ioNone, ioRead, ioWrite, ioStream, ioExit);
    PIocpRecord = ^TIocpRecord;
    TIocpRecord = record
    Overlapped: TOverlapped; //完成端口重叠结构
    WsaBuf: TWsaBuf; //完成端口的缓冲区定义
    IocpOperate: TIOCPOperate; //当前操作类型
    end;

    var FIocpRecv: PIocpRecord;

    iFlags, iTransfer: Cardinal;

    FIocpRecv.Overlapped.Internal := 0;
    FIocpRecv.Overlapped.InternalHigh := 0;
    FIocpRecv.Overlapped.Offset := 0;
    FIocpRecv.Overlapped.OffsetHigh := 0;
    FIocpRecv.Overlapped.hEvent := 0;
    FIocpRecv.IocpOperate := ioRead;
    iFlags := 0;

    WSARecv(FSocket, @FIocpRecv.WsaBuf, 1, iTransfer, iFlags, @FIocpRecv.Overlapped, nil);

    3)工作线程处理接收到的客户端数据

    3.1)工作线程是最繁忙,服务器上CPU的数量决定了工作线程的数量,CPU数量越多可以创建的工作线程数量也就越多。

    TClientSocket = record
    Lock: TCriticalSection;
    SocketHandle: TSocketHandle;
    IocpRecv: TIocpRecord; //投递请求结构体
    IdleDT: TDateTime;
    end;
    PClientSocket = ^TClientSocket;

    var
    ClientSocket: PClientSocket;
    IocpRecord: PIocpRecord;
    iWorkCount: Cardinal;

    GetQueuedCompletionStatus(FIocpHandle, iWorkCount, DWORD(ClientSocket), POverlapped(IocpRecord), INFINITE);

    IOCP大致的流程就是这样,当然服务端发送数据给客户端的代码此处省略,相信读者根据接收代码已经知道怎么弄了。

    IOCP为我们提供了一个系统级的消息队列(称之为完成队列),事件循环就是围绕着这个完成队列展开的。在发起IO操作后系统会进行异步处理(如果能立刻处理的话也会直接处理掉),当操作完成后自动向这个队列投递一条消息,不管是直接处理还是异步处理,最后总会投递完成消息。

    IOCP完成队列返回的消息是一个OVERLAPPED结构体和一个ULONG_PTR complete_key。complete_key是在用户将Socket handle关联到IOCP的时候绑定的,其实用性不是很大,而OVERLAPPED结构体则是在用户发起IO操作的时候设置的,并且OVERLAPPED结构可以由用户通过继承的方式来扩展,因此如何用好OVERLAPPED结构在螺丝壳里做道场,就成了封装好IOCP的关键。

  • 相关阅读:
    opencv —— copyTo 设置与操作感兴趣区域(ROI)
    opencv —— src.at<Vec3b>(i, j)[0]、src.at<uchar>(i, j)、src.ptr<uchar>(i) 访问图像的单个像素
    opencv —— getTickCount、getTickFrequency 计时函数
    opencv —— setMouseCallback 响应鼠标操作事件
    opencv —— createTrackbar、getTrackbarPos 滑动条的创建和使用
    opencv —— imread、namedWindow & imshow、cvtColor、imwrite 加载、显示、修改、保存图像
    redis缓存穿透,缓存击穿,缓存雪崩问题
    NoSQL技术
    日期处理
    WIN2008中部署网站后样式及JS加载不了(转载)
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/4605486.html
Copyright © 2011-2022 走看看