• Socket的综合应用总结


    Socket 的传输的内容大概分3种:

    1. 封装的结构体:结构体(结构清晰,发送数据占用内存小),例如

      struct SOCKETDATA

      {    

          DWORD password;        //每个客户端都有一个密码,为了防止外挂

          DWORD messageId;        //发送内容的ID标识,每种ID对应着消息的一种操作

          DWORD nowParkId;        //连续的ID号,防止丢包,和重复的发同样的包的操作

          Char* buffer;            //内容

      }此处也是对内容的简单的描述,真正的结构体可能更加复杂

    2. char*(显而易见的,支持指令),例如

      发送的字符串为"23,张三,10000",这样的内容的弊病在于很容易别解析,安全性低

    3. 超文本(支持脚本,类似xml,内存使用增加).可以结合1来综合运用,思路是很好的。

    收发机制:封包,粘包。Send SendList, recv RecvList

          向服务器一直发送数据的话,发送的内容不平均,有时内容较大,有时内容较小,一直连接对链路的负载也较大,所以我们在固定的时间间隔下发送数据,把要发送的数据存在一个链表中,集体发送,这样大大提高性能,发送的时间间隔根据游戏的情况而定,魔兽争霸为100ms,魔兽世界为200ms。

    线程管理:

    1、确保主线程不死

    2、一段时间间隔看子线程是否死亡

    3、Socket类封装:

        ServerSocket

        ClientSocket

        NetCom    (消息底层,可用socket或是DirectPlay等等)

        NetMessage(消息宏和结构体)

        NetManager(消息处理)

    4、Socket安全

    5、Socket其他功能:连包,粘包

        时间问题:加时间戳,逻辑不通过时间判断

    总结 Socket服务器端的设计:

    服务器还是利用Socket的完成端口来实现,首先创建服务器的Socket。

    然后创建Accept线程,当有客户端accept时,加入到客户端列表中,然后异步调用WSARecv。

    根据cpu个数创建接受数据的线程,接收的数据存储到链表中。

    处理接受到的数据时用一个单独的线程遍历其中的内容,用信号量控制。

    #define MAXMESSAGESIZE 1024
    
    enum SOCKETOPERATE
    {
        soRECV
    };
    
    struct SOCKETDATA
    {
       WSAOVERLAPPED        overlap;                    //重叠结构,用于异步请求的IO控制    
        WSABUF                Buffer;                        //缓存,用于异步请求数据的保存
        char                sMessage[MAXMESSAGESIZE];    //真正的缓存
        DWORD                dwBytes;                    //异步请求发生时,产生的字节流量
        DWORD                Flages;                        
        SOCKETOPERATE        OperationType;                //异步请求的操作类型
    
        void Clear(SOCKETOPERATE SO)    
        {
            ZeroMemory(this, sizeof(SOCKETDATA));
            Buffer.len = MAXMESSAGESIZE;
            Buffer.buf = sMessage;
            OperationType = SO;
        }
    };
    
    struct CClientPeer
    {
    
        SOCKET    ClientSocket;
        DWORD    ID;
        DWORD    PassWord;
        DWORD    NowPackID;
        DWORD    dwIP;
        u_short    Port;
        CEasyList *gClientRecv;
        void InitList()
        {
            gClientRecv = new CEasyList;
        }
    };
    
    class CServerSocket
    {
    public:
        CServerSocket(void);
        ~CServerSocket(void);
        bool Create(u_short Port);
        int SendBuffer(int ClientID, void* Buffer, int Size);
        int BroadCastBuff(void* Buffer, int Size);            //广播
    
    public:
        SOCKET        mSocket;
        CEasyList    mClients;
        HANDLE        mCP;
        u_short        mPort;
    };
    
    SOCKET CreateServerSocket(int Port);
    DWORD WINAPI SocketProcMain(LPVOID lpParam);        //Socket主线程函数,负责处理IO请求
    DWORD WINAPI SocketProcAccept(LPVOID lpParam);        //Socket线程函数,负责处理线程链接
    
    CServerSocket::CServerSocket(void)
    {
        WSADATA        wsaData;
        WSAStartup(0x0202,&wsaData);
        mSocket=INVALID_SOCKET;
        mCP = INVALID_HANDLE_VALUE;
    }
    
    CServerSocket::~CServerSocket(void)
    {
        closesocket(mSocket);
        WSACleanup();
    }
    
    SOCKET CreateServerSocket(int Port)
    {
    
        int iErrCode;
        WSADATA wsaData;
        iErrCode = WSAStartup(0x0202, &wsaData);
        int iRes;
        SOCKET tempSocket = socket(AF_INET, SOCK_STREAM, 0);
        SOCKADDR_IN addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(Port);
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        iRes = bind(tempSocket,(LPSOCKADDR)&addr,sizeof(addr));
        if(iRes == SOCKET_ERROR)
        {
            closesocket(tempSocket);
            return INVALID_SOCKET;
        }
    
        iRes = listen(tempSocket, 2000);
        if(iRes == SOCKET_ERROR)
        {
            closesocket(tempSocket);
            return INVALID_SOCKET;
        }
        return tempSocket;
    }
    
    bool CServerSocket::Create( u_short Port )
    {
        mSocket = CreateServerSocket(Port);
        if (mSocket == INVALID_SOCKET)
        {
            return false;
        }
    
        mPort = Port;
        mCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
        SYSTEM_INFO systemInfo;
        GetSystemInfo(&systemInfo);
        for (int i = 0; i<systemInfo.dwNumberOfProcessors;i++)
        {
            CreateThread(NULL, NULL, SocketProcMain, this, NULL, NULL);
        }
        CreateThread(NULL, NULL, SocketProcAccept, this, NULL, NULL);
        Sleep(5);
        return true;
    }
    
    DWORD WINAPI SocketProcMain(LPVOID lpParam)
    {
        HANDLE CP = ((CServerSocket*)lpParam)->mCP;
        SOCKET serverSocket = ((CServerSocket*)lpParam)->mSocket;
        DWORD dwBytes;
        SOCKETDATA *lpSocketData = NULL;
        while (1)
        {
            //等待全局完成端口的玩曾队列
            CClientPeer *pClient=NULL;
            GetQueuedCompletionStatus(CP, &dwBytes, (PULONG_PTR)&pClient, (LPOVERLAPPED*)&lpSocketData, INFINITE);
            if(dwBytes == 0xffffffff)
            {
                return 0;
            }
            //当数据类型为soRECV时,
            if(lpSocketData->OperationType == soRECV)
            {
                //当客端关闭连接时
                if(dwBytes == 0)
                {
                    closesocket(serverSocket);
                    HeapFree(GetProcessHeap(), 0, lpSocketData);
                }
                else
                {
                    char* s = new char[dwBytes+1];
                    strcpy(s, lpSocketData->sMessage);
                    pClient->gClientRecv->Add(s);
                    /*printf("Server:%d Client:%d 	:%s
    ", ((CServerSocket*)lpParam)->mPort,
                        pClient->dwIP/0x1000000,
                        lpSocketData->sMessage);*/
                    //重置Socket数据为soRECV
                    lpSocketData->Clear(soRECV);
                    WSARecv(pClient->ClientSocket, &lpSocketData->Buffer, 1, &lpSocketData->dwBytes, &lpSocketData->Flages, &lpSocketData->overlap, NULL);
                }
            }
        }
    }
    
    DWORD WINAPI SocketProcAccept(LPVOID lpParam)
    {
        SOCKET tmp;
        SOCKADDR_IN tempAddr;
        int dwAddrSize = sizeof(tempAddr);
        HANDLE CP = ((CServerSocket*)lpParam)->mCP;
        SOCKET serverSocket = ((CServerSocket*)lpParam)->mSocket;
        SOCKETDATA *lpSocketData;
        while(1)
        {
            tmp = accept(serverSocket, (sockaddr*)&tempAddr, &dwAddrSize);
            CClientPeer *pClient = NULL;    
            pClient = (CClientPeer*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CClientPeer));
            pClient->InitList();
            pClient->ClientSocket = tmp;
            pClient->dwIP = tempAddr.sin_addr.s_addr;
            pClient->Port = tempAddr.sin_port;
            ((CServerSocket*)lpParam)->mClients.Add(pClient);
            //创建客户端Socket和全局完成端口的链接
            CreateIoCompletionPort((HANDLE)tmp, CP, (DWORD)pClient, 0);
            //在主线程堆中开辟,Socket缓存数据
            lpSocketData = (SOCKETDATA *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKETDATA));
            //将此数据初始化为soRecv
            lpSocketData->Clear(soRECV);
            //异步调用WSARecv,其中第二个参数是I/O请求成功时,数据保存的地址。
            WSARecv(tmp, &lpSocketData->Buffer, 1, &lpSocketData->dwBytes, &lpSocketData->Flages, &lpSocketData->overlap, NULL);
        }
    }
    
    class CNetManager
    {
    public:
        CNetManager(void);
        ~CNetManager(void);
        bool CreateServer();
    
    private:
        CServerSocket mServerSocket;
    };
    
    DWORD WINAPI RecvProc(LPVOID pParam);
    
    bool CNetManager::CreateServer()
    {
        mServerSocket.Create(6565);
        CreateThread(NULL, NULL, RecvProc, &mServerSocket, NULL, NULL);
        return 1;
    }
    
    DWORD WINAPI RecvProc( LPVOID pParam )
    {
        if(pParam == NULL)
        {
            return 0;
        }
        CServerSocket *pServer = (CServerSocket*)pParam;
        CClientPeer *pClient;
        while(1)
        {
            for (int i=0; i<pServer->mClients.Count();i++)
            {
                pClient = (CClientPeer*)pServer->mClients.Get(i);
                if (pClient)
                {
                    for (int j=0; j<pClient->gClientRecv->Count();j++)
                    {
                        char* s = (char*)pClient->gClientRecv->Get(j);
                        printf("%x 	 (%d) %s 
    ",pClient->dwIP,j,s);
                        delete s;
                    }
                    pClient->gClientRecv->Clear();
                }
            }
        }
    }
  • 相关阅读:
    js中面向对象的写法
    js中防止全局变量被污染的方法
    移动端的头部标签和meta
    励志
    UX是什么?
    HTTP
    Django RestFramework (DRF)
    Vue(一)
    Vue-基础
    Vue-es6基础语法
  • 原文地址:https://www.cnblogs.com/renyuan/p/5100333.html
Copyright © 2020-2023  润新知