您的位置:首页 > 其它

Socket的综合应用总结

2016-01-04 21:34 155 查看
Socket 的传输的内容大概分3种:

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

{

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

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

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

Char* buffer; //内容

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

char*(显而易见的,支持指令),例如
发送的字符串为"23,张三,10000",这样的内容的弊病在于很容易别解析,安全性低

超文本(支持脚本,类似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 \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();
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: