TCP

TCP头部格式

截屏2021-08-01 上午10.41.13.png
截屏2021-08-01 上午11.17.47.png

序列号:建立连接时由计算机生成的随机数作为其初始值,用来解决网络报乱序的问题
确认应答号:下一次期望收到数据的序列号,用来解决不丢包的问题
控制位:ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建⽴连接时的 SYN 包之外该位必须 设置为 1 。 RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。 SYN:该位为 1 时,表示希望建⽴连接,并在其「序列号」的字段进⾏序列号初始值的设定。 FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双⽅的主 机之间就可以相互交换 FIN 位为 1 的 TCP 段。
TCP:是面向连接的、可靠的、基于字节流的传输层通信协议
面向连接:一对一的,不能一对多
可靠的:无论链路出现了什么样的变化,都可以保证一个报文能够到达接收端
字节流:消息是没有边界的,无论消息多大都可以传输;同时消息是有序的,当没有收到前一个消息的时候,即使收到了后面的消息,也不能扔给应用层去处理。
TCP四元组可以确定一个唯一的确定连接:源地址、源端口、目的地址、目的端口
连接包括了:socket、序列号和窗口大小

TCP与UDP的区别

  • 连接 TCP是面向连接的,传输数据之前需要建立连接,UDP不需要连接
  • 服务对象 TCP是一对一的,UDP可以是一对一、一对多、多对多
  • 可靠性 TCP是可靠交付数据的,UDP是尽自己最大努力进行交付
  • 拥塞控制、流量控制
  • 首部开销 TCP在没有使用选项的时候是20字节,而UDP的首部则只有8字节
  • 传输方式 TCP是流式传输,UDP是一个包一个包的发送,是有边界的,但是可能会丢包和乱序
  • 分片不同 TCP的数据大小如果大于MSS大小,会在传输层进行分片;而UDP的数据大小如果大于MTU,则会在IP层进行分层

TCP与UDP的应用场景

TCP:FTP文件传输、HTTP/HTTPS
UDP:适用于一些包总量较少的通信 DNS;视频、音频等多媒体通信;广播通信

TCP有首部长度字段,而UCP头部没有;UDP有包长度字段,而TCP字段则没有?
因为TCP的首部有一个选项导致长度是可变的,需要记录首部的长度,而UDP的首部长度则固定的八个字节
UDP其实包长度字段也是没有必要的,但是为了4的倍数就补充上了
TCP数据的长度=IP总长度-IP首部长度-TCP首部长度

TCP连接

截屏2021-08-01 上午11.21.27.png
一开始客户端和服务端都是处于closed状态,服务器主动监听某个端口,处于listen状态
三次握手:
第一次握手 客户端随机初始化序列号(client_isn),同时SYN标志位置为1,表示SYN报文;发给服务器,这个报文不包含应用层的数据,发送完了之后客户端处于SYN-SENT状态
第二次握手 服务端也随机初始化自己的序号,TCP首部的确认应答号(clinet_isn + 1),ACK和SNY置为1,不包含应用层的数据,之后服务器处于SYN-RCVD状态
第三次握手 客户端收到服务端的报文之后,返回一个应答报文,ACK为1,确认应答+1,这次是可以携带数据的;之后客户端处于EATABLISHED状态,客户端收到了之后也会处于EATABLISHED状态

为什么需要三次握手?

  • 避免历史连接(防止旧的重复连接初始化造成混乱) 由于网络阻塞等原因,一个旧的SYN报文可能会比新的SYN报文早到服务端,服务器返回了之后,客户端可以判断这个报文是否是历史连接,然后发送一个RST的报文给服务端进行连接的终止;如果两次握手则无法避免这种情况
  • 同步双方初始化的序列号 第三次握手才可以确保双方的初始化序列号都被可靠的同步;两次握手则只能保证一方的被同步接收了
  • 避免资源的浪费 如果只有两次连接的话,则可能会导致建立多个冗余的连接

四次握手:三次握手理论上就可以了,不需要使用更多的次数了

MTU:一个网络包的最大长度,一般为1500字节
MSS:除去IP和TCP头部之后,一个网络包所能容纳的TCP数据的最大长度

Linux内核中SYN与ACCEPT队列
截屏2021-08-01 下午7.59.45.png

如果应用程序过慢,就会导致Accept队列被占满
受到SYN攻击时,会导致SYN队列被占满
tcp_syncookies 的⽅式可以应对 SYN 攻击的⽅法:net.ipv4.tcp_syncookies = 1
截屏2021-08-01 下午8.03.23.png
当SYN队列满了之后,后续服务收到SYN包,不进入SYN队列;
计算出一个cookie值,再以SYN+ACK中的序列号返回客户端
服务端收到客户端的应答报文之后,服务器会检查这个ACK包的合法性,如果合法,直接放入到Accept队列
最后通过调用Accept() socket接口,从Accept队列取出连接

TCP断开连接

四次挥手
截屏2021-08-01 下午8.06.51.png

  • 客户端打算关闭连接,此时会发送⼀个 TCP ⾸部 FIN 标志位被置为 1 的报⽂,也即 FIN 报⽂,之后客户 端进⼊ FIN_WAIT_1 状态。
  • 服务端收到该报⽂后,就向客户端发送 ACK 应答报⽂,接着服务端进⼊ CLOSED_WAIT 状态
  • 客户端收到服务端的 ACK 应答报⽂后,之后进⼊ FIN_WAIT_2 状态
  • 等待服务端处理完数据后,也向客户端发送 FIN 报⽂,之后服务端进⼊ LAST_ACK 状态
  • 客户端收到服务端的FIN报文之后,回一个ACK应答报文,之后进入TIME_WAIT状态
  • 服务器收到了 ACK 应答报⽂后,就进⼊了 CLOSED 状态,⾄此服务端已经完成连接的关闭
  • 客户端在经过 2MSL ⼀段时间后,⾃动进⼊ CLOSED 状态,⾄此客户端也完成连接的关闭

只有主动关闭连接的,才会有TIME_WAIT状态

为什么TIME_WAIT等待时间是2MSL?
MSL是报文最大生存时间

  • 防止具有相同四元组的旧数据包被收到;

经过2MLS的时间,足以让两个方向的数据包都被丢弃了,使得之前连接的数据包在网络中都自然消失了,再出现的数据包一定都是新建立的连接产生的

  • 保证被动关闭连接的一方能够被正确的关闭

确保最后的ACK能够让被动方接收,从而帮助其正常的关闭

如果TIME_WAIT时间过短:最后一个ACK丢失了,服务端则会一直处于LAST_ACK;当客户端发起新建立连接的SYN报文之后,服务端会发送RST报文给客户端,建立连接的过程就会终止
如果TIME_WAIT时间过长:如果是服务器主动发起的,那么会导致内存资源的占用以及消耗一个本地端口

socket编程

//todo

TCP重传、滑动窗口、流量控制、拥塞控制

重传机制

超时重传
设定一个定时器,在超过制定的时间之后,没有收到对方的ACK报文之后,就会重发该数据
数据包丢失、确认应答丢失都是发生超时重传
RTT(Round-Trip Time)往返时延:数据从网络一端传送到另一端的时间
RTO(Retransmission Timeout 超时重传时间)
RTO太小:会导致可能是没有丢包但是就进行了重发,会增加网络的拥塞
RTO太大:很久之后才会重发,没有效率,性能差

快速重传
不以时间为驱动,而是以数据驱动重传
收到三次同样的ACK就会触发重传机制

SACK(Selective Acknow**ledgment)方法**
在TCP的头部选项中加一个SACK的东西,可以将缓存的地图发送给发送方,这样就可以只重传丢失的数据

Duplicate SACK(D-SACK)
主要是使用了SACK来告诉发送方有哪些数据被重复接收了

窗口大小(window)
这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据,所以窗口大小通常是由接收方的窗口大小来决定的。
发送方的滑动窗口

流量控制
//todo
让发送方根据接收方的实际接收能力控制发送的数据量,这就是流量控制

拥塞控制