简易游戏服务器—分包与粘包问题

简易游戏服务器—分包与粘包问题,第1张

粘包和分包是利用Socket在TCP协议下内部的优化机制。

1、什么是粘包

只有TCP有粘包现象,UDP永远不会粘包,为何,且听我娓娓道来。发送数据时间间隔很短,数据了很小,也就是发送数据比较频繁,会合到一起,产生粘包;

2、什么是分包

当我们发送的数据量很大的时候,可能是几千字节,TCP就会自动分开发送,其实说通俗点,就是你去拿快递,一看20个,一次拿不完,分几次拿!

3、总结

指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。

发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。

若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。

这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。分包是指在出现粘包的时候我们的接收方要进行分包处理。

在前面的测试程序中,是没有粘包问题的,这时候你可能有疑惑,我为啥数据会发送的特别快,我们以游戏服务器举例,比如游戏有联机对战功能,这时候肯定是需要同步位置信息的,这个频率是很快的,大约每秒就要40~80次,这个时候就会出现粘包问题。

其实很简单只要简单修改一下客户端即可。

1、程序测试 — 粘包问题

客户端:

服务器查看调试信息:

服务器已启动......

有一个客户端进行连接成功......

从客户端接收到的数据:0

从客户端接收到的数据:123

从客户端接收到的数据:4567

从客户端接收到的数据:8910

从客户端接收到的数据:1112131415

从客户端接收到的数据:161718

从客户端接收到的数据:192021222324

从客户端接收到的数据:25262728

从客户端接收到的数据:2930313233

从客户端接收到的数据:34353637

从客户端接收到的数据:38394041

从客户端接收到的数据:42434445

从客户端接收到的数据:46474849

从客户端接收到的数据:50515253

从客户端接收到的数据:5455565758

从客户端接收到的数据:59606162636465666768

从客户端接收到的数据:6970717273

从客户端接收到的数据:74757677

从客户端接收到的数据:78798081

从客户端接收到的数据:82838485

从客户端接收到的数据:86878889

从客户端接收到的数据:90919293

从客户端接收到的数据:9495969798

从客户端接收到的数据:99

(很明显数据没有发送100次)

1、程序测试 — 粘包问题

客户端:

服务器查看调试信息:

服务器已启动......

有一个客户端进行连接成功......

从客户端接收到的数据:

0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000撒大声地所多所多所多所多所多所多所多所多所多所000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000撒大声地所多所多所多所多所多所多所多所多所多所多所多所多所多所多撒大声地所 多所多所多所多所多所多所多所多所多所多所多所多所多所多撒大声地所多所多所多所多所多所多所多所多所多所多所多所多所多所多000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000----------------------------------------------------------------撒大声地所多所多所多所多所多所多所多所多所?

从客户端接收到的数据:

77777777777777777777777777777777777777777777777777777777790909090909090909090909090909090909090000000000000000000000000099

(可以看出服务器是分两次接收的,但其实只要static byte[] dataBuffer = new byte[1024]给的空间足够大,分包问题就可解决)

其实也很好解决,我们在发送数据的时候事先存储数据的长度,不过用来存储数据长度的内存大小需要指定好,否则就没法判断了。

假设我们现在的数据出现了粘包,如下图所示:

这里只是演示一下,如果只有连续发送4次数据,一般是不会出现粘包的,看上图橙色部分表示我们用一个int32类型储存数据的长度,蓝色部分为我们实际要发送的数据,现在发生了粘包,也就是这四条数据合在一起发送给了服务器,

此时这条数据的总大小为 4字节 * 4 + 5 + 7 + 10 + 4 = 42字节

我们通过读取4字节数据可以知道数据的实际长度,以第一个数据为例,我们读取4字节数据,知道了这个数据有5个字节,程序如下:

int data_length = BitConverter.ToInt32(_data, 0)

此时的data_length = 5此时我们就读取这5个字节的数据即可!

string s = Encoding.UTF8.GetString(_data, 4, 5)

然后我们截取数据,从源数据的第4 + 5的位置开始截取到一个新数组,新字节数组索引从零开始,此时新字节数据的长度为42 - (5 + 4);(下图为新字节数组)

Array.Copy(_data, 5 + 4, _data, 0, 42 - (5 + 4))

依次循环下去,粘包就被成功的分包了。当然这个不要忘记每次更新一下当前数据长度。

_curLength = _curLength - (data_length + 4)// _curLength = 42 - (5 + 4)

1、客户端

创建Message类,用于发送数据前做处理,使得首4字节储存数据长度。

Message:

Main:

2、服务端

创建Message类,解决粘包问题!

Main:

服务器查看调试信息:

服务器已启动......

有一个客户端进行连接成功......

解析到一条数据:0米

4

8

解析到一条数据:1米

4

630

解析到一条数据:2米

4

622

解析到一条数据:3米

4

614

解析到一条数据:4米

4

606

解析到一条数据:5米

4

598

解析到一条数据:6米

4

590

解析到一条数据:7米

4

582

解析到一条数据:8米

4

574

解析到一条数据:9米

4

566

解析到一条数据:10米

5

558

..............................................................................

解析到一条数据:98米

5

18

解析到一条数据:99米

5

9

有客户端退出.....

你解决个屁,异步接收的情况下,把_data数组调大点就完了,傻逼,咱们是做游戏!一般不会有分包问题!!

1-技术有什么区别

首先通信上目前的主流是HTTP协议和SOCKET这两种(HTML5提供了一种新的协议,WebScoket,对此了解并不多,因此不做评论,以免误导)。

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

(注:在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。)

Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

以J2SDK-1.3为例,Socket和ServerSocket类库位于http://java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。(摘自百科)

在WEB服务器中,一般情况是只需要使用HTTP协议的。因为它不太需要去与浏览器进行主动推送,只需要响应浏览器的访问就足够了

而在游戏服务器,这样的连接方式肯定是不够用的。很多时候游戏服务器是需要主动推送消息,如系统广播。

2-思维有什么区别

WEB服务器并不需要高频即时通讯,对响应速度要求不高。而游戏服务器,大多数是需要很及时的响应速度(暂不讨论弱联网游戏)。如DOTA,这种竞技类型的游戏,1秒就能发生很多事。

因此,在思考方向上,WEB服务器应该考虑是的多平台的兼容,大量用户访问的高并发。

而游戏服务器应该考虑的是高频通讯,高并发。

3-架构的侧重点有什么区别

在架构上面,一般访问量不是很大的网站是只有一台服务器的,访问量高的才会进行分布式设计或者集群设计。

而大部分游戏服务器都是需要分布式设计的。

在现有的网络游戏服务器端架构中,多是以功能和场景来划分服务器结构的。具体的划分是根据项目的需求进行的,并没有一个十分通用的架构。

以上是比较常见的结构,客户端登录的时候,连接GateServer,然后由GateServer去连接LoginServer进行登录。登录后通过CenterServer转发到GameServer(GameServer即是服务器大区)。

而其中的DCServer,主要的功能是缓存玩家角色数据,保证角色数据能快速的读取和保存。

LogServer便是保存日志的了。

4-本质有无区别

本质上,两者并无区别,只是需求不同,侧重点不同罢了。


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/316857.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-30
下一篇2023-04-30

发表评论

登录后才能评论

评论列表(0条)

    保存