TCP长连接的一些事儿

时间:2024-01-23 20:45:48

1、TCP的特点以及与应用

       TCP提供一种面向连接的、可靠的字节流服务。面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接。TCP建立连接需要经过三次握手,首先,客户端发送一段报文给服务器,表示我要连你,服务器收到报文后马上回复客户端,同意或者不同意你连我,最后客户端再发送一段报文给服务器表示确认要连接,经过这三次握手,客户端才能最终连接上服务器。TCP主要有这几个特点:TCP是面向连接的传输层协议;每个TCP连接只能有两个端点,而且只能是一对一通信,不能一点对多点直接通信;数据以字节流的方式传输;传输的数据无消息边界;TCP通过丢包重传、滑动窗口、数据排序、拥塞控制等机制来保证传输数据的可靠性。
       一般,强联网类型的游戏都使用TCP,当然,有些对实时性要求很高的游戏也会将TCP与UDP配合使用,比如王者荣耀这类MOBA游戏,在实时战斗中会使用可靠UDP协议,来保证游戏数据的高效传输,弱联网类型的一般使用HTTP,HTTP为短链接,它基于TCP协议,当完成一次通讯就会马上断开连接。
 

2、如何维护一个长连接

       在游戏过程中,玩家所处的网络环境是复杂多变的,有时因为糟糕的网络问题,会出现客户端掉线、延迟、丢包等问题,网络状况不好时UDP会丢包严重,TCP虽然是可靠,但也会在ip层丢包,只不过由于TCP的重传机制,能保证数据能传输到远程终端,但也会造成延迟。那么如何维护好一个长连接呢?那就是做好断线重连机制。
什么时候触发断线重连?
        第一种判断方法:在网络糟糕的情况下,在客户端进行网络操作时经常会显示的抛出异常,比如NetworkException、Timeout等,这种情况下,就说明客户端已经无法与服务器通讯,这时就需要客户端主动发起对服务器连接。
        第二种判断方法:使用心跳检测,可以服务器定时发心跳包给客户端,也可以客户端定时发心跳包给服务器,我认为最稳妥的应该是客户端发送心跳包给服务器,服务器收到后再回复一条确认收到的消息给客户端,心跳包只需简单设计就好,一般只包含消息头,不含消息体。当超过规定时间T没有收到心跳返回包时,就触发断线重连。
        第三种判断方法:使用Keepalive,keepalive是在TCP中一个可以检测死连接的机制,是由TCP协议层实现的,具体使用方法我暂时也没去了解~
        第四种判断方法:当手机客户端切回后台后一定时间又返回游戏,或者网络连接从wifi切到4G等情况下,可以主动触发一次重连操作。
断线重连的大致流程:        

3、如何处理粘包分包问题

       使用TCP协议做网络模块开发时,由于TCP本身的机制,如果发送的数据很小,那么会自动把一些小的数据合并在一起发送出去,但是接收端就没办法理解数据包并自行分开它,这就是粘包现象;而当一次发送的数据又过长的时候,TCP就会把该数据分成几部分发送出去,每次接收方就只会接受部分的数据,这就是分包现象。由于粘包和分包现象是在传输层发生的,我们只能在应用层想办法解决,处理粘包和分包有很多方法,这里介绍几种常见的方法:
       第一种方法是在每个数据包前面加上该数据包的长度,作为该数据包的包头,当收到消息包时,先读取消息包头,得到消息包的长度,再根据这个长度去读取对应长度的字节,这样就能很好的解决粘包分包的问题。
       第二种方法是在每个消息包的头部或尾部加上一个标识符,这个标识符不能是常用的字符,比如可以使用‘¥’这类平时不会出现在消息体中的字符,将它作为消息包界限,接收端在收到消息后就能根据这个界限方便的去做处理。
       第三种方法是如果使用的是JSON这类字符串做传输,可以根据消息包的首尾字符,通过判断首尾‘{’和‘}’来组成一个完整的消息包。
 

4、使用TCP还是UDP

       众所周知,C/S架构中,TCP使用广泛,UDP同样也有不少用武之地,先看一下UDP与TCP相比的区别:首先,UDP速度比TCP快,因为UDP不需要先与对方建立连接,也没有传输确认这一步骤,因此其速度会快不少;UDP一次性发送一个消息包,不需要考虑消息边界的问题;UDP可以使用广播或者组播的方式进行一对多传输;UDP由于没有可靠的消息传输机制,它的可靠性不如TCP,也不能保证有序传输。
       游戏往往对数据的可靠性传输要求很高,似乎看起来只能使用TCP,然而,很多游戏对客户端数据同步要求特别高,比如英雄联盟和王者荣耀,使用TCP来传输实时战斗数据是行不通的,那还是只能在UDP上想办法 ,通过对UDP进行一层可靠性封装,可以使UDP同样具有类似TCP传输可靠有序的特点。英雄联盟开发时使用的是Enet,ENet提供一个相对简单的以及稳健的位于UDP顶部的网络通信层。其提供的主要功能是可选的可靠按顺序的传送数据包。王者荣耀使用的似乎是KCP,KCP是一个快速可靠协议,它主要的设计目的是为了解决在网络拥堵的情况下TCP协议网络速度慢的问题,增大网络传输速率,但相当于TCP而言,会相应的牺牲一部分带宽。KCP没有规定下层传输协议,一般用UDP作为下层传输协议。提供类似这种可靠UDP服务的还有UDT等,在GitHub上可以找到不少这类项目。
 

5、使用哪种网络传输数据格式

       在做客户端服务器通讯前,要选择一种网络传输数据格式,在做选择时,都需要考虑几点:第一是数据大小,这关系到占用带宽和传输效率,应该尽量不要有多余的数据;第二点是可拓展性、可维护性,必须支持序列化及反序列化消息中用到的数据结构;第三点是数据安全性,有些数据是敏感数据,需要进行加密处理;第四点是是否跨平台支持;第五点是消息可读性,这点不重要,但消息可读性高的话,调试时方便。
       游戏开发中,主要的网络传输数据格式有Json、Xml、Protobuf,以及自定义二进制数据包,在很多时候,我们都使用Json和Xml,因为使用简单,可读性高,调试方便,但它们属于文本,占用空间稍大,显得有些臃肿。自定义二进制数据格式虽然体积小,传输高效,但致命的缺点是可扩展性差,在编写和解析数据包时很容易出错,而且当需要编写一些数据结构时会比较麻烦,因此这种方式只适合小项目。Protobuf是一种轻便高效的结构化数据存储格式,Protobuf可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式,目前提供了C++、Java、Python、C#等多种语言的API,很适合用于网络游戏的消息结构体定义上。相对于XML文件和Json文件,它的性能更好,效率更高。
 
如有错误,欢迎指正!