TCP-重叠IO-事件模型

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

#include 
#include 
#include 
#include 
#include 

#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[10000];
    OVERLAPPED IOeventall[10000];
};
#include "main.h"
#define  WSA_MAX_STR 1024


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);
    }
}


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重叠结构
        NULL                                //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重叠结构
        NULL                                //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()
{
    /*创建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();
        return 1;
    }
    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)
    {
        //这边可以优化,每1W个用一个线程
        for (DWORD Index = 0; Index < allsock.count; Index++)
        {

            /*等待事件:有事件产生后返回对应事件的下标*/
             DWORD  RET = WSAWaitForMultipleEvents(1, &allsock.IOeventall[Index].hEvent, false, 0, false);

            /*返回错误*/     /*等待超时检测*/
            if (WSA_WAIT_FAILED == RET || WSA_WAIT_TIMEOUT == RET)
            {
  /*              string ret = to_string(WSAGetLastError());
                MessageBoxA(0, "error_WSAWaitForMultipleEvents", ret.c_str(), 0);*/
                continue;
            }
       

            /*重叠IO换了新函数来接收消息*/
            DWORD lpcbTransfer;
            DWORD lpdwFlags;
            BOOL ret = WSAGetOverlappedResult(allsock.sockall[Index],//有信号的socket
                &allsock.IOeventall[Index],   //对应的重叠结构 
                &lpcbTransfer,              //0表示客户端下线,大于0表示接收或发送的实际字节数
                TRUE,                        //事件通知模型才能选true
                &lpdwFlags                   //接收WSArecv的参数5 flags
            );
            
            /*WSAGetOverlappedResult函数没有清空信号功能,故要自己清空*/
            WSAResetEvent(allsock.IOeventall[Index].hEvent);


            if (false == ret)
            {
                if (WSAECONNRESET == WSAGetLastError())
                {
                    printf("客户端强制下线\n");
                    //释放stocket
                    closesocket(allsock.sockall[Index]);
                    //关闭事件对象
                    WSACloseEvent(allsock.IOeventall[Index].hEvent);

                    //从数组中删掉
                    allsock.sockall[Index] = allsock.sockall[allsock.count - 1];//数组从0开始,-1才是正确位置
                    allsock.IOeventall[Index] = allsock.IOeventall[allsock.count - 1];//数组从0开始,-1才是正确位置
                    allsock.count--;
                    //由于数组最后一个数据被放在这个位置,还需要读取这个数据,所以循环需要减一
                    Index--;
                }
                continue;
            }



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

            if (0 != lpcbTransfer)
            {//通信中
                if (_recvstr[0] != 0)
                {
                    printf("通信中:recv:%s\n", _recvstr);
                    memset(_recvstr, 0, WSA_MAX_STR);
                    PostSend(Index);
                    //继续接收当前客户端消息
                    PostRecv(Index);
                }
                else
                {//send
                
                }
    
                continue;
            }

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

                //从数组中删掉
                allsock.sockall[Index] = allsock.sockall[allsock.count -1];//数组从0开始,-1才是正确位置
                allsock.IOeventall[Index] = allsock.IOeventall[allsock.count - 1];//数组从0开始,-1才是正确位置
                allsock.count--;
                //由于数组最后一个数据被放在这个位置,还需要读取这个数据,所以循环需要减一
                Index--;

   
                continue;
            }


        }
    }



    clear();


    //清理网络库
    WSACleanup();

    return 1;
}






int main()
{


    create("150.158.92.28");

    //  create("127.0.0.1");


    return 0;
}

新函数使用异步的方式,解决了老函数的阻塞问题,就是代码看的头疼,关闭客户端server接收不到消息

你可能感兴趣的:(github,java,前端)