赞
踩
那么, 如果超时的时间如何确定?
TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.
服务端状态转化:
客户端状态转化:
为什么是三次握手
为什么是四次挥手
双方关闭连接要经过双方都同意。
(1)为了保证客户端发送的最后一个ACK报文段能够到达服务器。即最后一个确认报文可能丢失,服务器会超时重传,然后服务器发送FIN请求关闭连接,客户端发送ACK确认。一个来回是两个报文生命周期。
如果没有等待时间,发送完确认报文段就立即释放连接的话,服务器就无法重传,因此也就收不到确认,就无法按步骤进入CLOSE状态,即必须收到确认才能close。
(2)防止已经失效的连接请求报文出现在连接中。经过2MSL,在这个连续持续的时间内,产生的所有报文段就可以都从网络消失。
在server的TCP连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的
在服务器程序中若将 new_sock.Close(); 这个代码去掉
们编译运行服务器. 启动客户端链接, 查看 TCP 状态, 客户端服务器都为ESTABLELISHED 状态, 没有问题.然后我们关闭客户端程序, 观察 TCP 状态
此时服务器进入了 CLOSE_WAIT 状态, 结合我们四次挥手的流程图, 可以认为四次挥手没有正确完成.
对每一个发送的数据段, 都要给一个ACK确认应答. 收到ACK后再发送下一个数据段.
这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候.既然这样一发一收的方式性能较低, 那么我们一次发送多条数据, 就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)
那么如果出现了丢包, 如何进行重传? 这里分两种情况讨论.
情况一: 数据包已经抵达, ACK被丢了—滑动窗口处理重传
这种情况下, 部分ACK丢了并不要紧, 因为可以通过后续的ACK进行确认:
1-1000字节数据已发送到主机B,但是1-1000的数据确认应答丢失。在收到确认序号ACK = 2001时,就可以把客户端1-1000的数据从缓冲中删除了,因为主机B已发送确认序号ACK = 2001,说明1-1000数据已经发送成功了
情况二: 数据包就直接丢了—高速重发控制
当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 "我想要的是 1001"一样;
如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;
这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中;
接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应.
因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control);
如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小.
一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率
那么所有的包都可以延迟应答么? 肯定也不是;
在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 “一发一收” 的. 意味着客户端给服务器说了 “How are you”, 服务器也会给客户端回一个 “Fine, thank you”;
那么这个时候ACK就可以搭顺风车, 和服务器回应的 “Fine, thank you” 一起回给客户端
创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;
- 调用write时, 数据会先写入发送缓冲区中;
- 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;
- 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
- 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
- 然后应用程序可以调用read从接收缓冲区拿数据;
- 另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工
由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如:
- 写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;
- 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;
在TCP报文段的首部中有一个“窗口大小”的字段,该字段占16bit=2byte。该字段主要用于TCP滑动窗口进行流量控制,很多人喜欢把TCP的MSS和“窗口大小”字段混淆,在这了做一个区分说明
MSS是TCP报文段中数据部分的最大长度,如果上层交付下来的数据超过MSS, 链路层就要对交付下来的数据进行分段, 得不偿失有一定成本(链路层帧数数据部分大小为1500byte, 你不商量大小底层就会自己封装)。在TCP连接的第一次、第二次握手中会分别告知对方MSS,从而起到通信双方协商MSS的效果。
TCP报文段首部中,“窗口大小”字段通常用于告知对方自己的能够接受的数据量大小。窗口本质就是一个缓冲区buffer,该字段的值用于告知对方自己剩余的可用缓冲区大小。在每一个TCP报文段中都会通过“窗口”字段告知对方自己的所能接收数据的大小。窗口大小通常用滑动窗口流量控制。
再举个例子,MTU 的大小就好像一座桥的承重吨位,而桥就相当于网卡;
事先给定 MSS,可以防止因为你货车载货过多,要进行分批运输;
如果不指定 MSS,一旦你货车超载,吨位超过桥的承重能力,你就得把货拆分成几批运过去,运过去之后你还得组装,这是得不偿失的;
文章参考: 点我看MSS的作用
首先要明确, 粘包问题中的 “包” , 是指的应用层的数据包.
那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界.
思考: 对于UDP协议来说, 是否也存在 “粘包问题” 呢?
为什么TCP这么复杂? 因为要保证可靠性, 同时又尽可能的提高性能.
可靠性:
提高性能:
其他:
UDP的应用场景
TCP的应用场景
如果只了解上两条你就废了, 实际中UDP由于传输速度快, 应用场景远大于TCP, 即使是在HTTP中, 最新的HTTP 3.0也已经投降了UDP. 常见QQ聊天也是UDP(网络不佳时自适应TCP), 但是需要在应用层做好数据丢失的解决方法. 故可以说
UDP才是永远滴神 !
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。