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回调:不存在无序问题,不存在无用功,但是同样也存在消耗大量线程的问题(高并发时每个客户端都要开启一个回调线程,线程占用比事件还多)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值