TCP协议
- TCP协议段格式
-
4位TCP报头长度:4位表示的最大10进制数为15,15*32/8= 60字节 所以报头最大长度为60字节
-
6位标志位:
- URG:紧急指针是否有效
- ACK:确认号是否有效
- PSH:提示接受端应用程序立即从TCP缓存区把数据读走
- RST:对方要求重新建立连接;携带RST标识的报文段称为复位报文段
- SYN:请求建立连接;携带SYN标识的称为同步报文段
- FIN:通知对方本端关闭,携带FIN为结束报文段
-
16位校验和:发送端填充,CRC校验,接收端校验不通过,则认为数据有问题,校验和不光包含TCP首部,也包含TCP数据部分
-
16位紧急指针:标识哪部分数据是紧急数据
-
TCP三次握手四次挥手
-
三次握手
- 调用socket,创建连接套接字
- 调用connect,向服务器发起连接请求
- connect发出SYN段并阻塞等待服务器应答(第一次握手)
- 服务端收到客户端的SYN后,会应答一个SYN-ACk段表示同意建立连接 (第二次握手)
- 客户端收到SYN-ACK后会从connect返回,同时应答一个ACK段
-
四次挥手
- 客户端调用close,发出FIN段(第一次挥手)
- 服务端收到FIN段,会应答一个ACK,同时服务端read会返回0 (第二次挥手)
- read返回0后,服务端知道客户端关闭了连接,所以也会close关闭连接,此时发出一个FIN段(第三次挥手)
- 客户端收到FIN段,再返回一个ACK给服务端
-
问题:
-
为啥需要第三次握手:
需要第三次握手的原因是如果A向B发送连接请求报文段,由于网络原因滞留了,直到连接释放后才到达B,这个报文段是一个失效的请求报文段,B误以为收到了新的连接请求,于是就向A发出了确认报文段,同意建立连接,但是A并没有发出建立连接的请求,因此不会理会B的确认,但B认为新的连接已经建立了,就会白白创建connfd浪费资源
-
为啥需要四次挥手
当服务端收到客户端发来的FIN段时,很可能不会立即调用close关闭socket,所以只能先发送一个ACK确认服务端已经收到,然后等服务端所有的报文全部发完,再调用close关闭socket并发送FIN段
-
为什么TIME_WAIT状态需要2MSL才能回到CLOSED状态
客户端接收到服务端发送的FIN端后,会向服务端发送一个ACK段,如果发送的这个ACK段在网络传输的过程中丢失了,那么服务端没有收到ACK段,就会继续重复发送FIN段,所以客户端不能立即关闭,必须确认服务端接收到了ACK
-
如果建立了连接,客户端故障了
TCP设有保活计时器,服务端每收到一次客户端请求都会重置这个计时器,时间通常是两小时,若两个小时还没收到客户端任何数据,服务端就会发送一个探测报文,以后每隔75秒发送一次,若连发10次没反应就关闭连接
-
-
确认应答机制(ack)
TCP是面向字节流传输,TCP将每个字节的数据都编号,即序列号,当发送数据时都会携带seq,如seq=1,同时接收端会有一个ack带有确认序号,表明收到了那些数据,下次从哪发送如:ack=1001;
-
超时重传
- 主机A发送数据给主机B,由于网络状况,数据无法到达主机B,如果主机A在一段时间内没有收到B发来的确认应答,就会重发
- 主机A没有收到主机B的ACK,也可能是ACK丢了,因此主机B会收到很多重复数据,所以TCP会通过序列号来去重
TCP会动态计算超时时间,超时以500ms为一个单位,每次判定超时重发的时间是500ms的整数倍
-
滑动窗口
- 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。
- 收到第一个ACK,滑动窗口向后移
- 操作系统内核为了维护这个滑动窗口,需要开辟发送缓存区来记录当前还有哪些数据没有应答,确认应答的数据,才能从缓冲区删掉
- 窗口越大,则网络的吞吐量就越高
丢包重传:
-
数据包递达,ACK丢了
不影响,可以通过后续的ACK确认
-
数据包丢了
当一段报文段丢失后, 发送端会一直收到服务端应答的丢失ACK的确认序号
如果发送端连续三次收到同一序号,就会将丢失的数据包重发
当接收端接收到正确的ACK后,再次返回的ACK应是丢失ACK+6 *(序列大小),其实之前发送的都已经收到了,被放在内核的接收缓冲区
-
流量控制
接收端处理数据的速度是有限的,当发送端发送数据过快导致接收区的缓冲区被装满,这个时候发送端继续发送就会发生丢包,继而引起重传等一系列连锁反应,因此通过窗口大小来控制发送端的发送速度,这种机制就叫做流量控制
- 接收端将自己可以接受的缓冲区大小 放入TCP首部的16位窗口大小字段,通过ACK段通知发送端
- 窗口大小越大,说明网络的吞吐量越高
- 接收端一旦发现缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端
- 发送端接收到这个窗口大小后,就会减缓发送速度
- 如果接收端缓冲区满了,就会将窗口置为0,这时发送端不会发送数据,但是要定期发送窗口探测数据,以便实时检测缓存区是否有空余
-
拥塞控制
在网络情况不明的情况下,贸然发送大量数据,将会是其更加拥堵, TCP引入慢启动,先发送少量的数据测试传输速度,再确定用多大速度传输数据
- 发送开始时定义拥塞窗口大小为1
- 每次收到一个ACK应答,拥塞窗口加1
- 每次发送数据包时 将拥塞窗口和接收端反馈的窗口大小做对比,取较小的值作为实际发送的窗口
- 当拥塞窗口大小小于阈值,将指数增长,否则线性增长
-
延迟应答
如果接收数据的主机立即返回ACK应答,返回的窗口可能较小,所以需要延迟,是窗口大小变大
数量限制: 每隔N个包应答一次
时间限制: 超过最大延迟时间就应答一次
-
捎带应答
客户端向服务端发送数据后,服务端也发送数据时,ACK就可以和数据包一起回传给客户端
-
面向字节流
- 调用send时,数据会先写入发送缓冲区中
- 如果发送的字节数太长,会被拆分成多个TCP数据包
- 如果发送的字节数太短,就会现在缓冲区里等待,等待缓冲区长度差不多,或者其他合适的时机发出去
- 接收数据时,数据也是从网卡驱动程序到达内核的接收缓冲区
- 然后应用程序可以调用read从接收缓冲区拿数据
- 另一方,TCP的连接,既有发送缓存区,又有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据
发出去
- 接收数据时,数据也是从网卡驱动程序到达内核的接收缓冲区
- 然后应用程序可以调用read从接收缓冲区拿数据
- 另一方,TCP的连接,既有发送缓存区,又有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据