TCP-重叠IO-回调机制

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

#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
//#define FD_SETSIZE 1024
	

#include <winsock2.h>
#include <windows.h>
#include <string>
#include <mswsock.h>
#include <iostream>

#pragma comment(lib,"Mswsock.lib")
#pragma comment(lib,"ws2_32.lib")

using namespace std;

//自定义的消息值不能和系统消息冲突
#define WSAAsyncSelectMsg    WM_USER +1

#define Sever_Port 9999



struct fd_es_set //事件模型
{
    UINT count;
    SOCKET sockall[1024];
    HANDLE eventall[1024];
};


struct fd_win_set//消息模型
{
    UINT count;
    SOCKET sockall[1024];
};


struct fd_esIo_set //重叠IO事件模型
{
    UINT count;
    SOCKET sockall[1024];
    OVERLAPPED IOeventall[1024];
};
#include "main.h"
#define  WSA_MAX_STR 1024

bool PostRecv(UINT Index);
bool PostSend(UINT Index);

fd_esIo_set allsock;

char _recvstr[WSA_MAX_STR]; //recv消息用的



VOID Server_write_error()
{

}

void clear()
{
    /*释放整个结构体,可能有些事件和socket已经被释放过了,不影响*/
    for (UINT i = 0; i < allsock.count; i++)
    {
        //释放stocket
        closesocket(allsock.sockall[i]);
        //关闭事件对象
        WSACloseEvent(allsock.IOeventall[i].hEvent);
    }
}


//重叠IO完成回调
void CALLBACK RecvCall(
    DWORD dwError, //错误码
    DWORD cbTransferred, //接收到的字节,为0代表客户端退出
    LPWSAOVERLAPPED lpOverlapped,  //接收重叠结构
    DWORD dwFlags //函数执行方式
)
{
    //获取当前socket和事件//得到当前数组下标
    UINT i = lpOverlapped - allsock.IOeventall  ;

    if (WSAECONNRESET == dwError || 0 == cbTransferred)
    {
        printf("客户端下线\n");
        
        //释放stocket
        closesocket(allsock.sockall[i]);
        //关闭事件对象
        WSACloseEvent(allsock.IOeventall[i].hEvent);

        //从数组中删掉
        allsock.sockall[i] = allsock.sockall[allsock.count - 1];//数组从0开始,-1才是正确位置
        allsock.IOeventall[i] = allsock.IOeventall[allsock.count - 1];//数组从0开始,-1才是正确位置
        allsock.count--; 
    }
    else
    {//接收到了数据
        printf("通信中:recv:%s\n", _recvstr);
        memset(_recvstr, 0, WSA_MAX_STR);
       // PostSend(i);
        //继续接收当前客户端消息
        PostRecv(i);
    }
}

//重叠IO完成回调
void CALLBACK SendCall(
    DWORD dwError, //错误码
    DWORD cbTransferred, //接收到的字节,为0代表客户端退出
    LPWSAOVERLAPPED lpOverlapped,  //重叠结构
    DWORD dwFlags //函数执行方式
)
{
    printf("通信中:sendover\n");
}


bool PostSend(UINT Index)
{

    string sendmsg = "服务器发送";
    WSABUF lpBuffers = { strlen(sendmsg.c_str()) ,(char*)sendmsg.c_str() };
    DWORD lpNumberOfBytesRecvd;
    DWORD  flag = 0;

    int ret = WSASend(allsock.sockall[Index],  //接受的客户端socket
        &lpBuffers,       //接收的缓冲区
        1,      //参数二的个数
        &lpNumberOfBytesRecvd,      //接收成功的话保存接收字节数量
        flag,                            //设置flag,默认0
        &allsock.IOeventall[Index],        //IO重叠结构
        SendCall                                //IO重叠回调例程
    );

    if (0 == ret)
    {//立即完成
      //  printf("send:立即完成\n");

        return 1;
    }
    else
    {
        int error = WSAGetLastError();
        if (ERROR_IO_PENDING == error)
        {//等待客户端链接中   延迟处理
            return 1;
        }
        return 0;
    }


}





bool PostRecv(UINT Index)
{

    WSABUF lpBuffers = { sizeof(_recvstr) ,_recvstr };
    DWORD lpNumberOfBytesRecvd;
    DWORD  flag = 0;

    int ret = WSARecv(allsock.sockall[Index],  //接受的客户端socket
        &lpBuffers,       //接收的缓冲区
        1,      //参数二的个数
        &lpNumberOfBytesRecvd,      //接收成功的话保存接收字节数量
        &flag,                            //recv参数5,默认0
        &allsock.IOeventall[Index],        //IO重叠结构
        RecvCall                                //IO重叠回调例程
    );

    if (0 == ret)
    {//立即完成
        printf("PostRecv立即完成:%s\n", lpBuffers.buf);

        memset(_recvstr, 0, WSA_MAX_STR);
        //继续接收当前客户端消息
        PostRecv(Index);
        return 1;
    }
    else
    {
        int error = WSAGetLastError();
        if (ERROR_IO_PENDING == error)
        {//等待客户端链接中   延迟处理 ,在主函数循环里
            return 1;
        }
        return 0;
    }


}




bool PostAccept()
{
    while (1)
    {
        /*创建socket和event,给客户端Socket和event加入数组*/
        allsock.sockall[allsock.count] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
        allsock.IOeventall[allsock.count].hEvent = WSACreateEvent();


        char getstr[WSA_MAX_STR];
        DWORD lpdwBytesReceived;
        /*返回true代表客户端立即连上了服务器*/
        bool res = AcceptEx(allsock.sockall[0],     //投递参数1服务器socket,
            allsock.sockall[allsock.count], //   异步接收服务器socket
            getstr,//缓冲区制作,接收新链接发送的第一条数据,之后数据用WSArecv接收了,鸡肋
            0,  //设置0 参数3无效了;
            sizeof(sockaddr_in) + 16,//为本地地址信息保留的字节数,此值至少比传输协议大16字节
            sizeof(sockaddr_in) + 16,//为客户端地址信息保留的字节数,此值至少比传输协议大16字节
            &lpdwBytesReceived,              //接受参数3数据的长度         
            &allsock.IOeventall[0]          //服务器的重叠结构
        );

        if (true == res)
        {//立即连接了

            //投递recv收信息
            PostRecv(allsock.count);
            //根据情况投递send
          //  PostSend(allsock.count);
            //链接成功后客户端数组++
            allsock.count++;
            //再次投递一个PostAccept  递归  我们这用循环替代
           // PostAccept();
          continue;
        }
        else
        {
            int error = WSAGetLastError();
            if (ERROR_IO_PENDING == error)
            {//等待客户端链接中 延迟处理
                return 1;
            }
            return 0;
        }
   }

}




/*1.打开网络库
* 2.校验网络库版本
* 3.创建SOCKET
* 4.绑定IP地址和端口
* 5.开始监听
* 6.创建客户端socket/接受链接
* 7.与客户端收发消息
* 8.(6.7)两步的函数accept,send,recv 有堵塞,可以用select解决,这种函数可以处理小型网络
*/
int create(const char* IpAdress)
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* 使用Windef.h中声明的MAKEWORD(低字节、高字节)宏 */
    wVersionRequested = MAKEWORD(2, 2);

    /*启用网络链接库,调用的封装库命令*/
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return -1;
    }

    /*确认WinSock DLL支持2.2*/

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        printf("Could not find a usable version of Winsock.dll\n");
        //清理网络库
        WSACleanup();
        return -1;
    }


    //创建套接字。 创建网络类型 tcp或者upd
   //SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    SOCKET socketServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);

    if (INVALID_SOCKET == socketServer)
    {
        string ret = to_string(WSAGetLastError());
        MessageBoxA(0, "error_socket", ret.c_str(), 0);
        //清理网络库
        WSACleanup();
        return -1;
    }
    //设置sockaddr结构
    sockaddr_in saServer;
    saServer.sin_family = AF_INET;
    saServer.sin_addr.s_addr = INADDR_ANY;
    saServer.sin_port = htons(Sever_Port);



    // 绑定本机(服务器)IP和端口
    //sockaddr结构中的信息
    if (SOCKET_ERROR == bind(socketServer, (SOCKADDR*)&saServer, sizeof(saServer)))
    {
        string ret = to_string(WSAGetLastError());
        MessageBoxA(0, "error_bind", ret.c_str(), 0);
        //释放stocket
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return -1;
    }

    /*监听本机(服务器)的套接字*/

    if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
    {
        string ret = to_string(WSAGetLastError());
        MessageBoxA(0, "error_listen", ret.c_str(), 0);
        //释放stocket
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return -1;
    }




    /*将服务器socket和事件对象句柄写进结构体*/
    allsock.IOeventall[allsock.count].hEvent = WSACreateEvent();;
    allsock.sockall[allsock.count] = socketServer;
    allsock.count++;


    初始化数据
    if (0 == PostAccept())
    {
        clear();
        //清理网络库
        WSACleanup();
        return 0;
    }



    while (true)
    {

        {

            /*等待事件:有事件产生后返回对应事件的下标*/ //参数6true 将等待事件函数和完成例程机制结合
            DWORD  RET = WSAWaitForMultipleEvents(1, &allsock.IOeventall[0].hEvent, 0, WSA_INFINITE,  true);

            /*返回错误*/     /*等待超时检测*/
            if (WSA_WAIT_FAILED == RET || WSA_WAIT_IO_COMPLETION == RET)
            {
                /*              string ret = to_string(WSAGetLastError());
                              MessageBoxA(0, "error_WSAWaitForMultipleEvents", ret.c_str(), 0);*/
                continue;
            }
            /*WSAGetOverlappedResult函数没有清空信号功能,故要自己清空*/
            WSAResetEvent(allsock.IOeventall[0].hEvent);

            {//服务器链接
                printf("服务器链接成功\n");
                //投递recv收信息
                PostRecv(allsock.count);
                //根据情况投递send
              //  PostSend(allsock.count);
                //链接成功后客户端数组++
                allsock.count++;
                //再次投递一个PostAccept  递归
                PostAccept();
   
            }

        }
    }



    clear();


    //清理网络库
    WSACleanup();

    return 1;
}






int main()
{


    create("150.158.92.28");

    //  create("127.0.0.1");


    return 0;
}

重叠IO事件:还是存在无序的问题,死循环挨个访问事件是否有触发(无用功很多浪费性能),如果事件过多,还需要开多线程优化访问,代码繁杂

重叠IO回调:不存在无序问题,不存在无用功,但是同样也存在消耗大量线程的问题(高并发时每个客户端都要开启一个回调线程,线程占用比事件还多)文章来源地址https://www.toymoban.com/news/detail-675914.html

到了这里,关于TCP-重叠IO-回调机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【单片机】STM32单片机的各个定时器的定时中断程序,标准库,STM32F103

    高级定时器和普通定时器的区别(https://zhuanlan.zhihu.com/p/557896041): TIM1是高级定时器,使用的时钟总线是RCC_APB2Periph_TIM1,和普通定时器不一样。 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用

    2024年02月07日
    浏览(58)
  • 【STM32】STM32单片机结构及部件原理

    STM32是目前比较常见并且多功能的单片机,要想学习STM32,首先要去了解它的基本构成部分以及各部分的原理。 单片机型号:正点原子STM32F103ZET6 目录 STM32内部结构总览图: 2.内部结构解析         1.内核 :STM32F103ZET6采用的是 ARM Cortex-M3 处理器,内核可以理解为单片机 处

    2023年04月08日
    浏览(51)
  • STM32单片机学习3--STM32控制键盘

    单片机型号:STM32F103C8T6 开发环境:Keil5 4种输入模式 上拉输入模式:在默认状态下(GPIO引脚无输入),读取得的GPIO引脚数据为1,高电平(与Vdd相连的为上拉电阻); 下拉输入模式:在默认状态下(GPIO引脚无输入),读取得的GPIO引脚数据为0,低电平(与Vss相连的为下拉电

    2024年02月10日
    浏览(60)
  • 【单片机】STM32单片机,定时器的输入捕获,基于捕获的频率计,STM32F103

    下面的定时器都具有输入捕获能力: 查看另一篇文章:https://qq742971636.blog.csdn.net/article/details/131471539 外部计数频率计的缺点:需要两个定时器配合,最高能测量的频率是否有限制我没具体尝试。 基于捕获的频率计的缺点:最高能测量的频率有限制。 TIM3_CH1 PWM PA6 10KHZ。 输入

    2024年02月14日
    浏览(58)
  • stm32系列单片机介绍

        stm32是基于ARM® Cortex®  内核的 32位微控制器和微处理器。常见的内核有:     Cortex-M0,代表型号STM32F0、STM32L0;     Cortex-M0+,代表型号STM32C0(23年新推出,主要针对低成本);     Cortex-M3,代表型号STM32F1、STM32F2、STM32L1;     Cortex-M4,代表型号STM32F3(混合信号)、

    2024年02月06日
    浏览(56)
  • 单片机简介(STM32介绍)

        单片机是 单片微型计算机 的简称,Mcu是Microcontroller的简称,也就是嵌入式微控制器。采用集成电路技术将具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、定时器/计时器、多种I/O口和中断系统等功能集成到一块硅片上。可以说单片机就是一个小而完善

    2024年02月16日
    浏览(41)
  • 单片机stm32智能鱼缸

    随着我国经济的快速发展而给人们带来了富足的生活,也有越来越多的人们开始养鱼,通过养各种鱼类来美化居住环境和缓解压力。但是在鱼类饲养过程中,常常由于鱼类对水质、水位及光照强度有着很高的要求,而人们也由于工作的方面而无法贴心的照料,因此经常因为水

    2024年02月22日
    浏览(45)
  • STM32-01-认识单片机

    单片机是什么? 单片机:Single-Chip Microcomputer,单片微型计算机,是一种集成电路芯片。 下面是电脑与单片机的对应关系: 单片机的特点? 体积小:5mmx5mm 功耗低:mA级 集成度高:IO、TIM、AD、DA 使用方便:C、Debug 拓展灵活:IIC、SPI、FSMC 单片机有什么用? 仪器仪表:电源、

    2024年02月03日
    浏览(47)
  • STM32单片机学习4--STM32控制八段码

    数码管:实际上是多个LED按照一定顺序排列,并加上遮罩所构成的元件。 八段码一般会引出9个引脚,其中7个引脚显示数字(或某些字母),1个显示小数点,1个作为片选端。 根据连接方式的不同,数码管分为 共阳 和 共阴 。 共阳在这端输出低电平时点亮,高电平时会熄灭

    2024年01月23日
    浏览(49)
  • stm32单片机/51单片机蜂鸣器不响(proteus模拟)

    1提高蜂鸣器电阻,这样根据分压原理,可以提升蜂鸣器2段电压 2更改蜂鸣器的工作电压为更小的值,这个可以通过在proteus内设置探针查看电压值,更改为其值就可以了 我觉得如果不是仿真的话,蜂鸣器额定电压是不能改的,如果电压不够的话好像就只能够选用合适的三极管

    2024年02月11日
    浏览(44)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包