TCP(Transmission Control Protocol),即传输控制协议,主要解决了数据在网络上的可靠传输问题。其数据流向大概为:应用层的数据会到TCP层的Segment中,然后TCP层的Segment会到IP层的Packet中,然后再到以太网Ethernet的Frame中,到达对端后,自底向上解析数据并交给应用方。
TCP、IP首部
TCP首部结构图:
一些字段说明:
- 序号(Sequence Number):用于解决包乱序问题
- 确认序号(Acknowledgement Number):用于解决不丢包问题
- 窗口大小(Window Size):用于解决流控问题
- 控制位(Control Flag):包的类型,用于状态机流转
- 首部长度(Data Offset):表示多少个4字节的首部长度,由于该值只占4个bit,所以首部最大长度为$4*15=60$字节
IP首部结构:
一些字段说明:
- 生存时间(TTL):控制Packet存活时间
- 标志位:用于控制Packet是否分片
TCP状态机
状态流转图:
三次握手
握手的目的在于建立连接及同步双方后续通信需要的信息,比如mss、ISN、window size等。
三次握手过程:
三次握手中还有个很重要的概念——TCP连接队列,其在服务端的作用:
Linux在2.2版本后用了两个队列来存放SYN_RCVD和EASTABLISHED状态的连接,其设置通过如下配置控制:
- syns queue:通过/proc/sys/net/ipv4/tcp_max_syn_backlog控制
- accept queue:取listen参数和/proc/sys/net/core/somaxconn两者之间的最小值
平时故障排查中,可以通过如下方式定位是否由连接队列引起的问题。
运行命令netstat -s:
其中overflowed指accept queue队列满了,sockets dropped指syns queue队列满了。
通过ss -nlt命令可以查看具体某个应用全连接队列大小及当前队列信息:
其中Send-Q表示全连接队列大小,Recv-Q表示当前等待进全连接队列或在全连接队列中的个数,如果Recv-Q大于Send-Q,则表示全连接队列已满,有部分连接无法处理。
关于全连接队列若满了,后续进队列连接逻辑控制由/proc/sys/net/ipv4/tcp_abort_on_overflow控制:
- 0:表示服务端直接扔掉客户端传过来的ack,虽然客户端已经状态已经是ESTABLISHED,但服务端连接并没有建立
- 1:发送客户端RST包
MTU、MSS
MTU(Maximum Transmit Unit),最大传输单元,物理接口(数据链路层)提供给其上层最大一次传输数据的大小,当前应用最多的物理接口是以太网。
MSS(Maximum Segment Size),最大TCP分段大小,不包含TCP首部和option,只包含TCP数据段。
这里有个问题,为什么我们需要MTU、MSS。
先来看看MTU,IP层的Packet到达数据链路层后转换为Frame,由网卡将每个Frame发出,那这里面就有两个值得关注的问题:不同Frame谁先谁后和每个Frame占用网卡的时间,这跟操作系统中的进程管理类似,不过有一点区别就在于Frame不具备拆分性。
至于谁先谁后的问题可看成简单队列问题,那每个Frame占用网卡时间怎么控制。
假设当前传输字节数为T,以太网头信息占14字节,尾部校验和FCS占了4字节,所以一个数据帧的传输效率为$\frac {T-18} T$,从式子中可以看出,T越大,传输效率越高,但是T越大发送每个Frame占用的时间也越多,所以以太网标准取T值为1518,其传输效率为$98.8\%$,这里的T值也就是MTU。
清楚了MTU后,在看下IP分片、重组。
数据包到达目的地期间可能需要经历多个中间设备,不同中间设备MTU值可能不同,这样就有可能面临数据包无法被传送的问题。针对这个问题,IP层引入了分片、重组技术,具体来讲就是将一个Packet进行分片,然后在接收端重组,这部分逻辑是由IP首部标志位控制。
MSS主要是TCP层用于分段依据,分段原因在于——若交由IP层分片,则有如下缺点:
- 某个分片丢失,导致分片前的整个数据包重新发送
- 分片、重组占用计算机开销也不低
所有交由上层自己控制也算是一种不错的选择,当然了,这部分是个人理解。
可靠性
TCP保证可靠性是通过消费确认机制实现,这里面涉及两个概念:超时定义、滑动窗口
数据发送超时时间并不是硬编码,而是通过某种算法动态计算出来的。
滑动窗口概念也很好理解,发送端发送多个数据段,然后等待接收端回传每个段确认消息即可。
关于超时定义算法、滑动窗口里面涉及到的东西并不少,暂时不做深究。
四次挥手
TCP是全双工通信,所以四次挥手也很好理解。
TCP状态图中进入TIME_WAIT后会等待$2MSL$,主要原因:
- 确保对方有足够的时间收到ACK,若没有收到,对端重发FIN,刚好$2MSL$
- 避免连接复用带来的问题
后续
暂时需要整理的就这些了,后面有用到的在补充。