博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Socket模型(三):Socket的综合应用

Posted on 2011-05-18 16:46  +石头+  阅读(658)  评论(0)    收藏  举报

1.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一段时间间隔看子线程是否死亡

2.Socket类封装:

    ServerSocket

    ClientSocket

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

    NetMessage(消息宏和结构体)

    NetManager(消息处理)

3.Socket安全

4.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 \t:%s\n", ((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 \t (%d) %s \n",pClient->dwIP,j,s);

                    delete s;

                }

                pClient->gClientRecv->Clear();

            }

        }

    }

}