iocp简单例子

这篇具有很好参考价值的文章主要介绍了iocp简单例子。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

首先说明:纯iocp使用的例子看:纯iocp例子(里面的代码可能无法运行,但是下面的代码一定可以运行,可以看看它里面的 PostQueuedCompletionStatus函数的使用,参考参考然后拿出来放到下面的代码里测试,搞几下就能懂了),主要涉及api:PostQueuedCompletionStatus(它可以触发一次iocp回调,也就是可以手动触发一次iocp回调,可以用来做多线程环境的锁)


带网络的例子:下方的代码是服务端,客户端后面写

下方代码观看方式(观看思路):

从main函数开始看,或者搜索 WSARecv、AcceptEx、WSASend、CreateIoCompletionPort、GetQueuedCompletionStatus这些关键字

相关函数说明:

WSARecv是注册一个接收数据的iocp,当接收到数据会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态,可以从第二个参数的内存地址得到接收的数据

AcceptEx是注册一个新连接的iocp,当新的连接连接成功后会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态

WSASend是注册一个发送数据的iocp,当发送成功会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态

CreateIoCompletionPort让一个socket绑定一个iocp

GetQueuedCompletionStatus获取一个iocp,如果没有iocp,将进入阻塞

iocp:iocp(完成端口)官方说明

大概意思就是当一个进程创建了iocp之后,操作系统会给创建一个队列,让这个队列与进程进行关联,专门给这个进程处理请求服务

服务端代码说明:

共涉及两个函数 iocp、ThreadFunc函数,从main函数开始看文章来源地址https://www.toymoban.com/news/detail-825792.html

#pragma comment(lib,"Ws2_32.lib")
#pragma comment(lib,"Mswsock.lib")
//#include <Windows.h>
#include <process.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <stdio.h>

int i = 0; // 这个不要管,写着玩的
char strarr[999999]{};// 接收数据的缓存,也就是 WSARecv 接收的数据

class COverlapped { // 这是重叠结构,用于iocp传参使用
public:
    /**
      重叠结构 就是 OVERLAPPED(它的中文意思是重叠,所以称它是重叠结构)
    */
    OVERLAPPED m_overlapped;
    DWORD m_operator;
    int clientIndex;
    char m_buffer[4096];
    COverlapped() {
        m_operator = 0;
        memset(&m_overlapped, 0, sizeof(m_overlapped));
        memset(m_buffer, 0, sizeof(m_buffer));
    }
};
HANDLE hIOCP;
SOCKET sock;
//SOCKET client;
SOCKET clients[5];
void iocp() {
    // SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); // TCP
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup failed.\n");
        return;
    }

    for (int i = 0; i < 5; i++)
    {
        clients[i] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    }

    // 创建一个套接字用于绑定iocp,让AcceptEx生效
    sock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    //client = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (sock == INVALID_SOCKET) {
        int ttt = WSAGetLastError();
        int a = ttt;
        return;
    }
    // 重建一个iocp
    hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, sock, 4);
    // SOCKET client = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    // sock与iocp绑定
    CreateIoCompletionPort((HANDLE)sock, hIOCP, 0, 0);

    // 下方是设置套接字ip与端口
    sockaddr_in addr;
    addr.sin_family = PF_INET;// PF_INET 与 AF_INET是一个东西,用哪一个都行,混着用也行
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");// 监听所有ip
    addr.sin_port = htons(9527);
    bind(sock, (sockaddr*)&addr, sizeof(addr)); // ip信息绑定socket
    listen(sock, 5);


    COverlapped overlapped{}; // 创建重叠结构
    overlapped.m_operator = 1; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用
    overlapped.clientIndex = 0;
    DWORD received = 0; // 固定写死
    // char buffer[4096] = "";

    // AcceptEx是异步的,调用之后很快就会返回,投递给iocp一个新连接事件
    if (AcceptEx(sock, clients[overlapped.clientIndex], overlapped.m_buffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &overlapped.m_overlapped) == FALSE) {

        // WSAGetLastError 获取上一个函数执行结果,只能针对套接字相关使用,它的返回值是上一个函数的执行状态,可以得到执行成功还是执行失败
        // 它有许多错误类型,可以去 msdn(微软官方文档简称msdn) 搜 WSAGetLastError,然后从 WSAGetLastError函数说明的页面里可以找到返回值都有哪些以及返回值说明
        if (WSAGetLastError() != WSA_IO_PENDING)
        {
            return;
        }
    }
    // 到这环境就搞完了,剩下的就是死循环,不要让主线程结束,等待客户端连接触发 AcceptEx 注册的iocp
    // 然后进入iocp回调,也就是 ThreadFunc 函数里的死循环,然后高性能使用iocp思路
    // 当有连接发送消息来了之后,第一时间想办法放缓存里,其余操作都不要做
    // 就只做一个数据放缓存的动作,然后开几个线程从缓存中获取数据然后再处理
    // iocp的优点就是响应快,可以当线程锁用,如果线程不安全的场景就没必要使用iocp
    // iocp再快也快不过无锁操作
}

int i2 = 1;

unsigned int __stdcall  ThreadFunc(void* a) {
    // 开启线程
    while (true)
    {
        LPOVERLAPPED pOverlapped{};
        DWORD transferred = 0;
        DWORD key = 0;

        // transferred可以得到有效字节数,也就是 WSARecv 接收数据的长度
        // 依据tcp或udp协议不同,数据大会造成分包发送,分包是顺序的,不用担心数据会乱,所以需要在发送的数据包里,设置数据包大小
        // 然后通过 transferred 判断数据包是否接收完毕,比如 transferred 它的数字小于,我们数据包里设置的大小,就说明数据包没有接收完毕,需要拼接数据
        // pOverlapped 获取重叠结构,重叠结构是用来使用 iocp 时进行iocp内部的传参
        // hIOCP 绑定了什么socket,它就触发什么,如果socket没有与它绑定,任何操作都不会触发 GetQueuedCompletionStatus 函数
        if (GetQueuedCompletionStatus(hIOCP, &transferred, &key, &pOverlapped, WSA_INFINITE)) {
            COverlapped* pO = CONTAINING_RECORD(pOverlapped, COverlapped, m_overlapped);

            switch (pO->m_operator)
            {
            case 1: {
                printf("AcceptEx\n");

                // 有客户端连接,这里把客户端连接绑定到 IOCP 里,如果不绑定,WSARecv会失效,WSASend可以发送,但是发送成功无法触发 GetQueuedCompletionStatus 函数的回调
                CreateIoCompletionPort((HANDLE)clients[pO->clientIndex], hIOCP, 0, 0);

                /*********************************************************************************************************************************************/
                DWORD Flags = 0; // 固定写死
                DWORD dwRecv = 0; // 固定写死
                COverlapped overlapped3{}; // 创建重叠结构,并初始化内存
                overlapped3.m_operator = 3; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用
                overlapped3.clientIndex = pO->clientIndex;
                WSABUF m_wsabuffer2{}; // 接收数据的缓冲区结构
                m_wsabuffer2.buf = strarr; // 设置缓冲区,也就是一块存放数据的内存,当Recv成功之后,会把数据写到这里
                m_wsabuffer2.len = 999999; // 内存大小,buf只是一块内存,只有内存地址没有大小,所以这里告知程序此处内存有多大
                // 添加Recv监听,告诉iocp只要有Recv操作,也就是有人给我发了消息你就触发 GetQueuedCompletionStatus 函数的回调
                // 然后通过 m_operator 就能知道这是什么操作,然后在对应的switch里处理
                WSARecv(clients[overlapped3.clientIndex], &m_wsabuffer2, 1, &dwRecv, &Flags, &overlapped3.m_overlapped, NULL);
                /*********************************************************************************************************************************************/



                /*============================================================================================================================================*/
                WSABUF m_wsabuffer{}; // 缓冲区,发送的数据内容
                DWORD m_received = 0; // 固定写死
                DWORD m_flags = 0; // 固定写死
                DWORD received = 0; // 固定写死
                COverlapped overlapped2{}; // 创建重叠结构
                overlapped2.m_operator = 2; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用
                overlapped2.clientIndex = pO->clientIndex;
                char* str = (char*)"000";
                m_wsabuffer.buf = str; // 发送的数据内容
                m_wsabuffer.len = strlen(str) + 1; // 数据大小,C++里的字符串是以0结尾,strlen(str)计算的长度不包含0,所以这里+1
                // 发送数据
                WSASend(clients[overlapped3.clientIndex], &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);
                /*============================================================================================================================================*/


                /*--------------------------------------------------------------------------------------------------------------------------------------------*/
                COverlapped overlapped{}; // 创建重叠结构
                overlapped.m_operator = 1; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用
                overlapped.clientIndex = i2++;
                // 当前是在 case 1 里,也就是当有了新的连接,它就会调用 GetQueuedCompletionStatus 函数的回调,然后再得到这是什么类型,如果类型是1就会进入 case 1 里
                // 下方的写法,首先AcceptEx是注册一个新连接事件(准确说是投递到iocp的队列里),也就是告诉iocp,当有了新连接就通知 GetQueuedCompletionStatus 函数的回调,通知完AcceptEx注册了事件将被删除
                // 被删除了,就说明下次没法触发了,所以这里再次注册 AcceptEx
                AcceptEx(sock, clients[overlapped.clientIndex], overlapped.m_buffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &overlapped.m_overlapped);
                /*--------------------------------------------------------------------------------------------------------------------------------------------*/
                i++;
                break;
            }

            case 2: { // 这里是 WSASend 成功的处理
                //printf("send %d--\n", i);
                printf("recv %d -- %s\n", i, strarr);
                WSABUF m_wsabuffer{}; // 缓冲区,发送的数据内容
                DWORD m_received = 0; // 固定写死
                DWORD m_flags = 0; // 固定写死
                DWORD received = 0; // 固定写死
                COverlapped overlapped2{}; // 创建重叠结构
                overlapped2.m_operator = 2; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用
                overlapped2.clientIndex = pO->clientIndex;
                char* str = (char*)"000";
                m_wsabuffer.buf = str; // 发送的数据内容
                m_wsabuffer.len = strlen(str) + 1; // 数据大小,C++里的字符串是以0结尾,strlen(str)计算的长度不包含0,所以这里+1
                // 发送数据
                WSASend(clients[overlapped2.clientIndex], &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);
                break;
            }
            case 3: { // 这里是 WSARecv 成功的处理
                printf("recv %d -- %s\n", i, strarr);
                DWORD Flags = 0; // 固定写死
                DWORD dwRecv = 0; // 固定写死
                COverlapped overlapped3{}; // 创建重叠结构,并初始化内存
                overlapped3.m_operator = 3; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用
                overlapped3.clientIndex = pO->clientIndex;
                WSABUF m_wsabuffer2{}; // 接收数据的缓冲区结构
                m_wsabuffer2.buf = strarr; // 设置缓冲区,也就是一块存放数据的内存,当Recv成功之后,会把数据写到这里
                m_wsabuffer2.len = 999999; // 内存大小,buf只是一块内存,只有内存地址没有大小,所以这里告知程序此处内存有多大
                // 添加Recv监听,告诉iocp只要有Recv操作,也就是有人给我发了消息你就触发 GetQueuedCompletionStatus 函数的回调
                // 然后通过 m_operator 就能知道这是什么操作,然后在对应的switch里处理
                WSARecv(clients[overlapped3.clientIndex], &m_wsabuffer2, 1, &dwRecv, &Flags, &overlapped3.m_overlapped, NULL);
                break;
            }
            default:
                break;
            }
        }
    }
    return 0;
}
int main() {

    // 初始化 iocp 环境
    iocp();

    // iocp处理事件线程,主要看 ThreadFunc函数 里的case 1里的代码,看懂了它,就懂iocp网络了
    _beginthreadex(NULL, 0, ThreadFunc, (LPVOID)hIOCP, 0, NULL);

    // 避免主线程结束,导致程序结束
    while (true)
    {

    }
    return 0;
}

到了这里,关于iocp简单例子的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 网络知识点之-路由表

    本文章收录至《网络》专栏,点击右上角专栏图标可访问本专栏! 在计算机网络中,路由表(routing table)或称路由择域信息库(RIB, Routing Information Base),是一个存储在路由器或者联网计算机中的电子表格(文件)或类数据库。路由表存储着指向特定网络地址的路径(在有些

    2024年02月12日
    浏览(48)
  • 网络开发过程详细知识点

     网络生命周期 至少包括系统构思与计划、分析和设计、运行和维护的过程。  常见的迭代周期分为四阶段周期、五阶段周期、六阶段周期。   网络开发过程根据五阶段迭代周期模型可被分为 五个阶段:    需求分析、现有网络分析、确定网络逻辑结构、确定网络物理结构

    2024年02月07日
    浏览(38)
  • 5分钟掌握接口自动化测试,4个知识点简单易学!

    接口测试是一种软件测试方法,用于验证不同软件组件之间的通信接口是否按预期工作。在接口测试中,测试人员会发送请求并检查接收到的响应,以确保接口在不同场景下都能正常工作。 就工具而言,常见的测试工具有Jmeter、Postman等。但这类工具往往更是做接口调试,对

    2024年02月05日
    浏览(58)
  • 网络知识点之-OSPF协议

    本文章收录至《网络》专栏,点击右上角专栏图标可访问本专栏! OSPF(Open Shortest Path First开放式最短路径优先)是一个内部网关协议(Interior Gateway Protocol,简称IGP),用于在单一自治系统(autonomous system,AS)内决策路由。是对链路状态路由协议的一种实现,隶属内部网关协议(

    2024年02月13日
    浏览(34)
  • 计算机网络知识点整理

    1. 五层协议 应用层 :通过应用进程间的交互来完成特定网络应用。HTTP、DNS 、SMTP等协议。数据单位为报文。 传输层 :负责向两台主机中进程的通信提供数据传输服务。TCP(面向连接,可靠传输,单位为报文段)、UDP协议(无连接,尽最大努力传输,单位是用户数据报)。

    2023年04月08日
    浏览(89)
  • 网络安全的相关知识点

    1.窃听:广播式网络系统。 2.假冒 3.重放:重复一份报文或者报文的一部分,以便产生一个被授权的效果。 4.流量分析 5.数据完整性破坏 6.拒绝服务 7.资源的非授权使用 8.陷门和特洛伊木马:木马病毒有客户端和服务端两个版本,不具备自我复制性。 9.病毒:自我复制。 10.诽

    2024年02月13日
    浏览(39)
  • 网络知识点之-BGP协议

    本文章收录至《网络》专栏,点击右上角专栏图标可访问本专栏! 边界网关协议(BGP)是运行于 TCP 上的一种自治系统的路由协议。 BGP 是唯一一个用来处理像因特网大小的网络的协议,也是唯一能够妥善处理好不相关路由域间的多路连接的协议。 BGP 构建在 EGP 的经验之上。

    2024年02月16日
    浏览(43)
  • 网络知识点之-RIP协议

    本文章收录至《网络》专栏,点击右上角专栏图标可访问本专栏! RIP协议,指路由信息协议(Routing Information Protocol),是基于距离矢量算法的路由协议,利用跳数来作为计量标准。 中文名:路由信息协议 外文名:Routing Information Protocol 特点:利用跳数来作为计量标准 类别:

    2024年02月13日
    浏览(38)
  • 网络知识点之-DHCP协议

    本文章收录至《网络》专栏,点击右上角专栏图标可访问本专栏! DHCP协议指动态主机配置协议 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议) ,是 RFC 1541(已被 RFC 2131 取代)定义的标准协议,该协议允许服务器向客户端动态分配 IP 地址和配置信息。 DHCP协议支持C

    2024年02月09日
    浏览(40)
  • Linux网络编程(一-网络相关知识点)

    目录    一、网络相关知识简介 二、网络协议的分层模型 2.1 OSI七层模型 2.2 TCP/IP五层模型 2.3 协议层报文间的封装与拆封  三、IP协议 3.1 MAC地址  3.2 IP地址 3.3 MAC地址与IP地址区别 互联网通信的本质是数字通信,任何数字通信都离不开通信协议的制定,通信设备只有按照约定

    2024年01月24日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包