zoukankan      html  css  js  c++  java
  • 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();
                }
            }
        }
    }
  • 相关阅读:
    python 的基础 学习 第六天 基础数据类型的操作方法 字典
    python 的基础 学习 第五天 基础数据类型的操作方法
    python 的基础 学习 第四天 基础数据类型
    ASP.NET MVC 入门8、ModelState与数据验证
    ASP.NET MVC 入门7、Hellper与数据的提交与绑定
    ASP.NET MVC 入门6、TempData
    ASP.NET MVC 入门5、View与ViewData
    ASP.NET MVC 入门4、Controller与Action
    ASP.NET MVC 入门3、Routing
    ASP.NET MVC 入门2、项目的目录结构与核心的DLL
  • 原文地址:https://www.cnblogs.com/renyuan/p/5100333.html
Copyright © 2011-2022 走看看