Untiy 网络编程-深入了解TCP

发布时间 2023-07-21 11:28:58作者: CatSevenMillion

1.从TCP到物理层

  应用层

    应用层是给应用程序提供功能的。在发送{hello}的例子里面,计算机会把hello转化为二进制然后发送到传输层。

  传输层

    在收到二进制数据后,传输层协议会对它进行一系列的加工,并提供数据流传送可靠性校验流量控制。又由于网络层的IP包大小有限制(65535),所以还需要有数据拆分的机制,把TCP包拆分为多个IP包。

  网络层

    这一层会把IP包根据目的地址把本地的包路由到目的地。

  网络接口层

    IP协议会继续封装成链路层协议做一些数据校验后,再通过物理介质传输到接收方。

2.数据传输流程

  1)TCP连接的建立

    TCP连接的过程也称为三次握手。

 

    值得注意的是,在第三次握手时,可能由于网络原因,导致第三次握手要等待很久才能返回,因为底层的超时重传机制会导致Server没有收到第三次握手会不断重复发送第二次握手数据包。直到连接成功或者超出重试次数。当双方都ESTABLISHED则连接成功。

  2)TCP数据传输

    TCP传输,在发送方并不能确保数据被对方收到,于是发送会等待接收方回应。如果太长时间没有收到回应也会启用重传机制重发数据。

    同时,发送数据时,TCP会考虑对方缓存区的容量,当对方满时会暂时停止发送数据。同时TCP还会根据返回数据的时间判断当前网络是否“通畅”,以减缓发送报文的间隔。

 

  3)TCP连接终止

    TCP的连接终止称为“四次挥手”

 

3.常用的TCP参数

  1)ReceiveBufferSize:

    指定了接收端缓冲区的大小,当接收端缓冲区满的时候,发送端会暂停发送数据。较大的缓冲区可以减少发送端暂停的概率,提高发送效率。

  2)SendBufferSize:

    指定了发送端缓冲区的大小。对于没有“完整发送数据的项目”(前一个博客),可以使用扩大发送区的方式优化项目性能。

  3)NoDelay:

    对于实时性要求较高的游戏,需要将该值设置为true。Socket在默认情况下使用的是Nagle算法。

    对于传统的TCP连接,假设我们要发送{hello}。经过TCP/IP协议封装之后就成为了{ IP头|TCP头|hello }。总数据量变成了20+20+5=45个字节。假设现在用户频繁的一个一个的发送数据,总数据量就变成了41*5=205字节。比原来多了160字节。

    Nagle算法机制在于,如果发送端多次发送包含少量字节的数据包时,发送端不会立马发送数据,而是累积到一定数量后再发送。这样虽然会提高网络传输效率,但是对实时性也会有影响。所以实时网络游戏要关闭Nagle算法,即打开NoDelay

  4)TTL:

    TTL表示IP包所能经过的最大跳数。TTL的最大作用是防止IP包在网络中无限循环。在游戏开发中为了防止偏远地区的用户无法接收数据,可以尝试提高TTL值来解决问题。

  5)ReuseAddress:

    端口复用,即让同一个端口可以被多个socket使用。一般情况下,一个端口只能由一个进程独占,假设服务端程序都绑定了1234端口,若开启两个服务端程序,虽然,第一个程序能够成功绑定并监听,但是第二个会无法绑定并释放端口。我们知道TCP释放端口会经过大量的时间(四次挥手并等待重发)。或者当服务器崩溃时,Socket也不会立即释放,会等待大量时间重启。

    而我们想要要求,当服务器崩溃时,能够立即重启。端口复用的用途就是,防止服务器重启时,之前绑定的端口还未释放或者程序突然退出导致没有释放的端口。如果使用端口复用就可以启动新的服务器进程时重新绑定新的端口。使用如下:

socket.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReuseAddress,true);

  6)LingerState:

    这个关键字是用来设置套接字的保持时间的。即四次挥手时,第二次到第三次之间的等待发送数据的时间。

4.心跳机制   

  TCP连接的正常端口是需要接收FIN报文的,但是如果网络状况不好,虽然已经断开了连接,但是对端依然会认为连接有效,依然占有资源。我们需要使用心跳机制来解决这个问题。

  Socket默认的心跳机制只用将KeepAlive开启即可。但是该方法很鸡肋,默认的等待时间是两个小时。