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, );
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, );
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, , );
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
for (int i = ; i<systemInfo.dwNumberOfProcessors;i++)
{
CreateThread(NULL, NULL, SocketProcMain, this, NULL, NULL);
}
CreateThread(NULL, NULL, SocketProcAccept, this, NULL, NULL);
Sleep();
return true;
} DWORD WINAPI SocketProcMain(LPVOID lpParam)
{
HANDLE CP = ((CServerSocket*)lpParam)->mCP;
SOCKET serverSocket = ((CServerSocket*)lpParam)->mSocket;
DWORD dwBytes;
SOCKETDATA *lpSocketData = NULL;
while ()
{
//等待全局完成端口的玩曾队列
CClientPeer *pClient=NULL;
GetQueuedCompletionStatus(CP, &dwBytes, (PULONG_PTR)&pClient, (LPOVERLAPPED*)&lpSocketData, INFINITE);
if(dwBytes == 0xffffffff)
{
return ;
}
//当数据类型为soRECV时,
if(lpSocketData->OperationType == soRECV)
{
//当客端关闭连接时
if(dwBytes == )
{
closesocket(serverSocket);
HeapFree(GetProcessHeap(), , lpSocketData);
}
else
{
char* s = new char[dwBytes+];
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, , &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()
{
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, );
//在主线程堆中开辟,Socket缓存数据
lpSocketData = (SOCKETDATA *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKETDATA));
//将此数据初始化为soRecv
lpSocketData->Clear(soRECV);
//异步调用WSARecv,其中第二个参数是I/O请求成功时,数据保存的地址。
WSARecv(tmp, &lpSocketData->Buffer, , &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();
CreateThread(NULL, NULL, RecvProc, &mServerSocket, NULL, NULL);
return ;
} DWORD WINAPI RecvProc( LPVOID pParam )
{
if(pParam == NULL)
{
return ;
}
CServerSocket *pServer = (CServerSocket*)pParam;
CClientPeer *pClient;
while()
{
for (int i=; i<pServer->mClients.Count();i++)
{
pClient = (CClientPeer*)pServer->mClients.Get(i);
if (pClient)
{
for (int j=; 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();
}
}
}
}

Socket的综合应用总结的更多相关文章

  1. Socket通信综合示例

    1用户注册客户单选择'用户注册',提示要输入用户名,密码,确认密码,校验成功后将用户信息保存到数据库,并提示数据库注册成功,请登录2用户登录客户端选择'登录以后'后,提示输入用户名和密码,验证成功则提 ...

  2. 【Socket编程】Java通信是这样炼成的

    简介 网络无处不在,移动互联时代也早已到来,单机版程序慢慢的已没有生命力,所有的程序都要能够访问网络,比如 QQ 网络聊天程序.迅雷下载程序等,这些程序都要同网络打交道,本次将与各位小伙伴们分享的就是 ...

  3. 实现TCP、UDP相互通信及应用

    实验名称  Socket编程综合实验(1) 一.实验目的: 1.理解进程通信的原理及通信过程 2.掌握基于TCP和UDP的工作原理 3.掌握基本的Socket网络编程原理及方法 二.实验内容 1.掌握 ...

  4. 基于.NET Socket API 通信的综合应用

    闲谈一下,最近和客户进行对接Scoket 本地的程序作为请求方以及接受方,对接Scoket 的难度实在比较大,因为涉及到响应方返回的报文的不一致性,对于返回的报文的格式我需要做反序列化的难度增大了不少 ...

  5. 综合实战--文件上传系统【JDBC&IO&Socket】

    本文纯属记录第一次实战遇到的坑和知识,如果后边有时间再做整理. 1,先写了个操作数据库的工具类SqlTool,照着JDBC资料打完之后,测试的时候出错了,java.lang.ClassNotFound ...

  6. 局域网象棋游戏(C++实现,使用Socket,界面使用Win32,CodeBlocks+GCC编译)

    目录 成果 运行效果图 过程 1. 首先的问题是下棋的两端应该是什么样的? 2. 接下来的问题是怎么表示,怎么存储? 3. 然后应该怎么通信呢? 代码 main.cpp chinese_chess.h ...

  7. Android和Linux应用综合对比分析

    原文地址:http://www.cnblogs.com/beer/p/3325242.html 免责声明: 当时写完这篇调查报告,给同事看了后,他觉得蛮喜欢,然后想把这篇文章修改一下,然后往期刊上发表 ...

  8. c#socket编程基础

    Microsoft.Net Framework为应用程序访问Internet提供了分层的.可扩展的以及受管辖的网络服务,其名字空间System.Net和System.Net.Sockets包含丰富的类 ...

  9. php综合应用

    php面试题之五--PHP综合应用(高级部分) 五.PHP综合应用 1.写出下列服务的用途和默认端口(新浪网技术部) ftp.ssh.http.telnet.https ftp:File Transf ...

随机推荐

  1. 使用word2vec训练中文词向量

    https://www.jianshu.com/p/87798bccee48 一.文本处理流程 通常我们文本处理流程如下: 1 对文本数据进行预处理:数据预处理,包括简繁体转换,去除xml符号,将单词 ...

  2. Spring(八):Spring配置Bean(一)BeanFactory&ApplicationContext概述、依赖注入的方式、注入属性值细节

    在Spring的IOC容器里配置Bean 配置Bean形式:基于xml文件方式.基于注解的方式 在xml文件中通过bean节点配置bean: <?xml version="1.0&qu ...

  3. Extend一个web application没有反应怎么办?

      通过SharePoint管理中心Extend一个web application的时候, 点完确定按钮后,没有反应,怎么回事? [解决方法] 多等一会,不要连续点. 等待的过程中看看iis, 过一会 ...

  4. vscode关闭后未打开上次界面的解决办法

    1.更新vscode至最新版,当前时间2017-06-20,vs最新版1.13.1 2.关闭vscode的正确方式,不是点击左上角的叉叉,而是CMD+Q或者在Dock右键退出 3.这样,你在下次打开v ...

  5. DOpus 10.5 使用帮助

    在线手册 http://www.dearopus.com/ http://resource.dopus.com/ http://www.gpsoft.com.au/help/opus10/ 应急截图编 ...

  6. PPT模板中的”书签”

    引言 在项目中生成文档报告经常需要word中,其中的关键就是书签,通过定位和替换书签中的值来达到生成定制的报告(详见Word模板中的表格处理):但在PPT中却没有书签这个概念,所以,不能采用这种方式. ...

  7. ArcGIS10.3新体验

    自2012年ESRI更新10.2以后,终于在2014年12月8日,官方推出了10.3版本,前几天忙于抢票,今天终于可以在虚拟机中体验一把. 由于使用的是预览版,所有安装包只有800多M,包括桌面核心程 ...

  8. Unity3D for Mac 破解

    每次版本更新又忘记怎么搞了.干脆记下来! 1安装Unity 2安装成功后打开Applecation>Unity>Unity(右击显示包内容)>contents>MacOS 3用 ...

  9. Springmvc之接受请求参数二

    Springmvc之接受请求参数 准备工作 新建一个表单提交 请求地址: http://localhost:8080/ProjectName/user/login.do <form action ...

  10. 算法笔记_224:夺冠概率模拟(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 足球比赛具有一定程度的偶然性,弱队也有战胜强队的可能. 假设有甲.乙.丙.丁四个球队.根据他们过去比赛的成绩,得出每个队与另一个队对阵时取胜的概率表 ...