本期问题
此项目通信方面:
Socket采用TCP模式面向连接传输。
粘包机制:若持续有发送的内容,则会延迟发送本次信息,等待下一次的信息一起发送。信息粘结。从而导致无法把多个信息区分开。
想法
封装每次信息,包头包含信息长度。
接收方获取信息长度,如果收到的消息长度已经满足了此信息长度则从缓存区截取出对应长度的信息并去处理请求。
被截断后剩余的信息继续等待并重复这个操作。
服务端
服务端代码结构:
Server类为服务器开启Socket服务。
Client类为客户端实例对象。
Message类为与客户端通信的类,解决TCP粘包机制与信息收发。
ControllerManager通过收到的消息,跳转到对应的Controller,通过反射机制,找到对应的方法应答socket信息。
具体的Controller:调用对应的Dao与数据库进行数据交换。原路返回结果直到发送回客户端。
服务端功能:
登录、注册。(略)
公告:
玩家以排位模式连接到socket服务端后,自动发送公告,前端显示在UI上。
战绩存储:
对局结束后自动发送战绩到服务端。
排行榜查询:
Unity端可以查询各个游戏的排行榜(全服前5)。
粘包机制关键代码
private byte[] buffer = new byte[1024];//缓冲区
private int startindex;//buffer存到了第几位,用于解决粘包问题
//收到消息处理
//至于为什么有4,见下面那个封装方法
public void ReadBuffer(int len, Client client)
{
startindex += len;
if (startindex <= 4)
return;
int count = BitConverter.ToInt32(Buffer, 0);//读取包头获取消息长度count
while(true)//没必要while??
{
if (startindex >= (count + 4))//从0到startindex至少包含一个完整的消息,提取出来并把之后的消息内容复制到最开始防止buffer超界。
{
Console.WriteLine("socket信息够一个包长了,handleRequest");
MainPack msgPack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(Buffer, 4, count);
startindex -= (count + 4);
//剩余消息置在缓冲区最开始
Array.Copy(buffer, count + 4, buffer, 0, startindex);
//处理消息
Server.controllerManager.HandleRequest(msgPack, client);
}
else
{
break;
}
}
}
//相应发送消息时需要打一下包(计算信息长度放在包里一起发送过去)
public byte[] ParcelPack(MainPack messagePack)
{
byte[] packBytes = messagePack.ToByteArray();
byte[] head = BitConverter.GetBytes(packBytes.Length);
return head.Concat(packBytes).ToArray();
}
// head是一个int(实际信息长度)转为的byte[],所以实际占用4个byte。
图表说明
总结
通过对信息包的再次封装(记录信息长度并放在包的头部4字节)再发送,接收方通过这个int可以区分开包之间的界限。
此次开发熟悉了protocolbuffers的用法以及tcp的解决方法。