共计 6151 个字符,预计需要花费 16 分钟才能阅读完成。
本篇文章为大家展示了怎么进行 TCP 异常关闭的问题分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
研究测试 TCP 断开和异常的各种情况,以便于分析网络应用(比如 tconnd)断网的原因和场景,帮组分析和定位连接异常掉线的问题,并提供给 TCP 相关的开发测试人员作为参考。
各个游戏接入都存在一定的掉线问题,而且有的游戏项目的掉线比例还比较高,现在互娱自研游戏的网络接入基本上都用的是 tconnd 和 ProtocalHandler 组件,因此参与其掉线原因分析和研究。
在参与 A 项目的掉线问题研究分析过程中,tconnd 增加了玩家每个连接的流水日志和 ProtocalHandler 增加了每个连接的 Qos 上报日志,通过这些日志记录了每一次连接的断开原因和相关统计数据,其中包括了连接异常断开时 TCP 的底层错误码。
通过对 tconnd 的流水日志和 ProtocalHandler 的 Qos 日志进行统计分析,发现连接异常断开时 TCP 的错误码大部分是“104: Connection reset by peer”(Linux 下)或“10054: An existing connection was forcibly closed by the remote host”(Windows 下),单纯从错误码本来来说,大家都明白是“网络被对端重置了”,但究竟什么情况下会导致这种情况呢?因此就对 TCP 的各种关闭情况做了进一步的测试研究。
一. TCP 异常关闭的研究测试 1. 服务器端只 Recv 消息而不 Send 消息 1.1 测试方法
服务器程序在接受客户端的 TCP 连接后 Sleep 几秒钟,客户端程序在 TCP 连接后立即发送很多消息给对端后做相应动作(退出或等待),服务器程序 Sleep 完后开始 Recv 消息。
注意:服务器程序测试了 Linux 和 Windows 版本,但客户端只测试了 Windows 版本,如果是 Linux 客户端则有些 Case 的结果会不一样。
1.2 测试 Case
客户端程序正常运行的情况下,拔掉网线,杀掉客户端程序
目的:模拟客户端死机、系统突然重启、网线松动或网络不通等情况。
结论:这种情况下服务器程序没有检测到任何异常,并最后等待“超时”才断开 TCP 连接。
客户端程序发送很多数据包后正常关闭 Socket 并 exit 进程 (或不退出进程)
目的:模拟客户端发送完消息后正常退出的情况。
结论:这种情况下服务器程序能够成功接收完所有消息,并最后收到“对端关闭”(Recv 返回零)消息。
客户端程序发送很多数据包后不关闭 Socket 直接 exit 进程
目的:模拟客户端程序退出而忘记关闭 Socket 的情况(比如通过 Windows 窗口的关闭图标退出进程,而没有捕获相应关闭事件做正常退出处理等)。
结论:这种情况下服务器程序能够收到部分 TCP 消息,然后收到“104: Connection reset by peer”(Linux 下)或“10054: An existing connection was forcibly closed by the remote host”(Windows 下)错误。
客户端程序发送很多数据包的过程中直接 Kill 进程
目的:模拟客户端程序崩溃或非正常方式结束进程(比如 Linux 下 kill -9 或 Windows 的任务管理器杀死进程)的情况。
结论:这种情况下服务器程序很快收到“104: Connection reset by peer”(Linux 下)或“10054: An existing connection was forcibly closed by the remote host”(Windows 下)错误。
2. 服务器端 Recv 消息并 Send 应答消息 2.1 测试方法
服务器程序在接受客户端的 TCP 连接后 Sleep 几秒钟,客户端程序在 TCP 连接后立即发送很多消息给对端后做相应动作(退出或等待),服务器程序 Sleep 完后开始 Recv 和 Send 消息。
注意:服务器程序测试了 Linux 和 Windows 版本,但客户端只测试了 Windows 版本,如果是 Linux 客户端则有些 Case 的结果可能会不一样。
2.2 测试结果
客户端程序发送很多数据包后正常关闭 Socket 并 exit 进程(或不退出进程)
目的:模拟客户端正常关闭 Socket 后,服务器端在检查到 TCP 对端关闭前向客户端发送消息的情况。
结论:这种情况下服务器程序接收和发送部分 TCP 消息后,在 Send 消息时产生“32: Broken pipe”(Linux 下)或“10053: An established connection was aborted by the software in your host machine”(Windows 下)错误。
客户端程序发送很多数据包后不关闭 Socket 直接 exit 或 Kill 进程
目的:模拟客户端程序退出而忘记关闭 Socket、或客户端程序崩溃或非正常方式结束进程的情况。
结论:这种情况下服务器程序在 Recv 或 Send 消息时产生“104: Connection reset by peer”(Linux 下)或“10054: An existing connection was forcibly closed by the remote host”(Windows 下)错误。
3. 效果和总结 3.1 总结
TCP 发现网络异常(特别是 Linux 下的 104 错误或 Windows 下 10054 错误)的情况很多,比如网络本身的问题、中间路由器问题、网卡驱动器问题等不可抗拒因素,但下面是应用程序本身可能会导致的问题,也是我们需要进一步研究和解决的情况,特别是程序崩溃导致问题:
当 TCP 连接的进程在忘记关闭 Socket 而退出、程序崩溃、或非正常方式结束进程的情况下
(Windows 客户端),会导致 TCP 连接的对端进程产生“104: Connection reset by peer”(Linux 下)或“10054: An existing connection was forcibly closed by the remote host”(Windows 下)错误。
当 TCP 连接的进程机器发生死机、系统突然重启、网线松动或网络不通等情况下
-(Windows 客户端),连接的对端进程可能检测不到任何异常,并最后等待“超时”才断开 TCP 连接。
当 TCP 连接的进程正常关闭 Socket 时,对端进程在检查到 TCP 关闭事件之前仍然向 TCP 发送消息
(Windows 客户端),则在 Send 消息时会产生“32: Broken pipe”(Linux 下)或“10053: An established connection was aborted by the software in your host machine”(Windows 下)错误。
3.2 效果
针对 A 项目的掉线问题,通过问卷调查和联系个别玩家等方法,发现掉线的情况很大部分是客户端程序直接退出了,因此推动项目组实现了客户端的 Qos 上报功能,最后通过客户端的 Qos 上报的统计数据得出客户端程序的崩溃比例比较高,占了总掉线的很大比率,当然其它情况也存在,但比例相对比较小。
因此,A 项目首先应该解决客户端程序的崩溃问题,如果该问题解决了,也就解决大部分掉线问题。
二. TCP 异常关闭的进一步研究测试 1. 背景
B 项目游戏在跨服跳转时的掉线比例比较高,经过分析 ProtocalHandler 和 tconnd 的日志,发现掉线出现的情况是:tconnd 发送了跨服跳转消息后立即关闭了 Socket,客户端进程在接收到跨服跳转消息之前发送消息后收到 Windows 10054 错误,然后做断线重连失败。
B 项目实现跨服跳转的流程是 GameSvr 给客户端程序下发的跨服跳转命令的同时携带了 Stop 请求,也就是说 tconnd 在向客户端转发跨服跳转消息后立即就会关闭当前的 Socket 连接,而且 B 项目的客户端程序会定期不断地向服务器上报消息。这又怎么会导致客户端程序收到 10054 错误而呢?鉴于此,对 TCP 的连接做进一步的场景测试分析。
2. TCP 异常进一步测试研究 2.1 测试方法
客户端和服务器端程序建立 TCP 连接,服务器程序在 TCP 缓冲区中有消息或没有消息的情况下关闭 Socket,客户端在对端 Socket 已经关闭的情况下继续 Send 和 Recv 消息。
注意:服务器端只测试了 Linux 版本,但客户端测试了 Windows 和 Linux 两个版本。
2.2 测试结果
服务器端已经 close 了 Socket,客户端再发送数据
目的:测试在 TCP 对端进程已经关闭 Socket 时,本端进程还未检测到连接关闭的情况下继续向对端发送消息。
结论:第一包可以发送成功,但第二包发送失败,错误码为“10053: An established connection was aborted by the software in your host machine”(Windows 下)或“32: Broken pipe,同时收到 SIGPIPE 信号”(Linux 下)错误。
服务器端发送数据到 TCP 后 close 了 Socket,客户端再发送一包数据,然后接收消息
目的:测试在 TCP 对端进程发送数据后关闭 Socket,本端进程还未检测到连接关闭的情况下发送一包消息,接着接收消息。
结论:客户端能够成功发送第一包数据(这会导致服务器端发送一个 RST 包 已抓包验证),客户端再去 Recv 时,对于 Windows 和 Linux 程序有如下不同的表现:
Windows 客户端程序:Recv 失败,错误码为“10053: An established connection was aborted by the software in your host machine”。
Linux 客户端程序:能正常接收完所有消息包,最后收到正常的对端关闭消息(这一点与 Window 下不一样,RST 包没有被提前接收到)。
服务器端在 TCP 的接收缓冲区中还有未接收数据的情况下 close 了 Socket,客户端再收包
目的:测试在 TCP 的接收缓冲区中还有未接收数据的情况下关闭 Socket 时,对端进程是否正常。
结论:这种情况服务器端就会向对端发送 RST 包,而不是正常的 FIN 包(已经抓包证明),这就会导致客户端提前(RST 包比正常数据包先被收到)收到“10054: An existing connection was forcibly closed by the remote host”(Windows 下)或“104: Connection reset by peer”(Linux 下)错误。
3 效果和总结 3.1 总结
TCP 应用程序某些看是正常的行为下也可能会导致对端接收到异常,比如当 TCP 接收缓冲区中还有未收数据的情况下关闭 Socket,则会导致对端接收到异常关闭而不是正常关闭;反过来说,当 TCP 检测到异常关闭时并不一定表示业务上出问题了,因为很可能就是业务正常结束了。下面是本次测试的主要结论:
当 TCP 连接的对端进程已经关闭了 Socket 的情况下,本端进程再发送数据时,第一包可以发送成功(但会导致对端发送一个 RST 包过来):之后如果再继续发送数据会失败,错误码为“10053: An established connection was aborted by the software in your host machine”(Windows 下)或“32: Broken pipe,同时收到 SIGPIPE 信号”(Linux 下)错误;之后如果接收数据,则 Windows 下会报 10053 的错误,而 Linux 下则收到正常关闭消息。
TCP 连接的本端接收缓冲区中还有未接收数据的情况下 close 了 Socket,则本端 TCP 会向对端发送 RST 包,而不是正常的 FIN 包,这就会导致对端进程提前(RST 包比正常数据包先被收到)收到“10054: An existing connection was forcibly closed by the remote host”(Windows 下)或“104: Connection reset by peer”(Linux 下)错误。
3.2 效果
B 项目跨服跳转的掉线问题有相当一部分的种情况是 tconnd 向客户端转发跨服跳转消息后立即关闭 Socket 连接,而此时刚好客户端向 tconnd 发送了数据包:
第一种情况:tconnd 在关闭 Socket 的时刻其 TCP 的接收缓冲区中有未收的消息,这就使得 tconnd 进程的 TCP 向客户端发送的是 RST 包而不是正常结束的 FIN 包,所以客户端程序就会提前收到 RST 包(RST 包会比正常数据提前收到),而不会先收完跨服跳转消息后再接收到正常结束消息,这就导致客户端收到网络异常断线而做重连,但之前的连接是 tconnd 主动关闭的,所以不可能重连成功,从而导致掉线。
第二种情况:tconnd 已经关闭了 Socket 后,客户端在接收到跳转消息和检测到 TCP 关闭之前向 tconnd 发送了消息,这就会导致客户端程序收到异常断线而做重连并失败。
最后,与 B 项目项目组一起讨论,改进了大部分跨服跳转的业务流程后,掉线比例 j 减少了很多,当然还是存在一定比例的掉线,但这应该就是其它一些原因了(网络异常问题原因很多,国内当前的网络环境下,掉线问题是不可能完全避免的)。
通常情况下,向 TCP 的 Socket 发送完数据后关闭 Socket,大家认为这样很正常的方式肯定没有问题,对端应该正确收完数据后收到 TCP 的关闭消息,但实际上在某些情况下并非如此:当 TCP 本端的接收缓冲区中有未收的数据时关闭 Socket,这会导致对端收到 RST 的异常关闭消息;当对端在本端已经关闭 Socket 的情况下再次发送消息时,也会导致对端收到异常关闭消息;还有为了避免 TIME_WAIT 而设置了 SO_LINGER 选项的话,也会导致连接提前夭折使对端收到 RST 异常关闭消息。
有些时候业务流程对是否引起掉线也很重要(特别是连接关闭流程),比如前面的 B 项目的跨服跳转掉线问题很大部分就是因为 GameSvr 请求关闭连接导致的。建议各个游戏项目的关闭流程(包括跨服跳转的原连接的关闭)最好都由客户端发起关闭,这样就一定程度上避免上述问题的发生(因为客服端发起关闭的时候,一般业务流程都走完了,服务器端也不会再向客户端发送消息了)。
程序收到网络异常的情况很多(最多的就是 Linux 下的 104 错误和 Windos 下的 10054/10053 错误):有网络本身的问题、也有应用使用不当的问题;有运营商之间的跨网络问题、网络中间路由器问题、玩家机器硬件(比如网卡及其驱动)问题和操作系统、杀毒软件、防火墙等软件问题,还有玩家的上网设备和路由器等中间设备问题等,但客户端程序崩溃问题有可能会占掉线的很高比例,这也是值得我们注意和改进的地方。还有种情况值得我们注意,有些 TP-LINK 的路由器,当 UDP 包大小超过其 MTU 值时会导致用户机器的网络断开,从而引起掉线(这个问题在某些项目的个别玩家中已经出现过)。
网络异常关闭引起掉线是当前游戏中普遍存在的问题,区别只在于掉线的比例多少,特别是国内各运营商之间跨网络访问更是不太顺畅,要将其完全消除是不可能的,但我们的目标是将其控制在较小的可接受范围内。
上述内容就是怎么进行 TCP 异常关闭的问题分析,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注丸趣 TV 行业资讯频道。