摘要
TCP头部格式
TCP
什么是TCP?
TCP是面向连接的(只能一对一)、可靠的(确保每一个报文都能到达接收端)、基于字节流(保证字节的有序性,自动去除重复字节)的传输层通信协议。
为什么需要TCP协议?
由于网络层(IP层)是不可靠的,它不保证网络包的交付,不保证网络包的顺序,不保证网络包数据的完整性,因此需要上层传输层的TCP协议来保证。
TCP是传输层中的可靠服务,它能够保证网络包无损坏、无间隔、非冗余和顺序性。
什么是TCP连接
TCP连接就是用于保证可靠性和流量控制维护的某些状态信息,比如以下信息:
如何确定唯一一个TCP连接
四元组:
源地址和目的地址存在于IP头部中,用于IP协议;源端口和目的端口号存在于TCP头部中,用于表明报文发送到主机的哪个进程上。
理论上的服务器的TCP连接数可以达到:
最大连接数(2^48) = 客户端的IP数量(2^32)* 端口数量(2^16)
当然这只是理论,服务器的最大连接数还要受文件描述符和内存的限制:
UDP和TCP的区别?
TCP和UDP的应用场景
TCP三次握手
TCP三次握手前的客户端和服务端的初始状态均为CLOSED状态,服务端在监听某个端口以后会变为LISTEN状态。
上图是执行netstat -lantp命令获取的结果,在我服务器上我监听了80和443端口,状态位LISTEN。
TCP第一次握手
客户端在发送第一次握手报文时,会随机初始化序列号(client_isn),该序列号会被放置在TCP报文中的序列号中,同时SYN位置1,客户端在发送完该报文以后,会处于SYS_SENT状态。
上图是我通过WireShark抓取的第一次握手报文,可以看出以下信息:
TCP的第二次握手
服务端收到客户端的SYN报文后,也随机初始化自己的序列号(server_isn),该序号会被放置在TCP报文的序列号中,其次还会把确认应答号置为client_isn+1,接着会把ACK标志位和SYN标志位置1,服务端然后把该报文发送给客户端,发送完成以后,服务端处于SYN_RCVD状态。
上图是我通过WireShark抓取的第二次握手报文,可以看出以下信息:
TCP的第三次握手
客户端收到服务端的报文后,会回复服务端一个报文。该报文中应答号置为server_isn+1,并且将ACK标志位置为1,客户端在发送完报文以后会变为ESTABLISHED状态,服务器在收到报文后也会进入ESTABLISHED状态。
上图是我通过WireShark抓取的第三次握手报文,可以看出以下信息:
TCP第三次握手是可以携带数据的,前两次握手不能携带数据。TCP三次握手以后,客户端和服务端就可以正常发送数据了。
TCP握手为什么需要三次?
TCP三次握手如何阻止历史连接初始化
假如一个旧的SYN报文比最新的SYN报文到达,此时服务端回复SYN+ACK报文给客户端,客户端在收到报文以后会判断回复报文中的应答号,通过应答号发现这是一个历史连接,就会发送一个RST报文,中止历史连接。
如果是两次握手的话,不能判断连接是否是历史连接,三次握手就可以在收到第二次握手报文时精准的判断是否是历史连接。
TCP三次握手同步双方的初始化序列号
TCP通信的双方必须要各自维护一个序列号,序列号的主要作用如下:
通信双方必须要发送各自的初始化序列号才给对方,通过对方的ACK报文确定SYN报文已经接收,看上去四次握手也可以达到效果,但由于服务端的ACK报文和SYN报文可以合并在一个请求中给客户端,因此通过三次握手就可以同步双方的序列号,而且减少了一次请求耗时。
TCP三次握手避免资源浪费
如果是两次握手,假设客户端的SYN报文发生延时阻塞,客户端没有收到来自服务端的ACK报文,就会重发SYN报文,服务器也不清楚客户端是否收到了自己的ACK报文,因此只要收到客户端的SYN报文就必须建立连接,这样会建立多个冗余无效的连接,造成资源的浪费。
初始化序列号如何生成
ISN = M + F
分片
MTU和MSS的区别
IP层分片的缺点
在IP层中,如果发现数据包超过了MTU的大小,就会进行分片,但是如果一个分片丢失,由于IP层本身没有超时重传机制,需要借助传输层TCP来负责超时重传,此时就需要重传整个TCP报文,也就是IP所有的分片都需要重传,可以看出效率低下。
所以为了解决这种问题,通常避免在IP层分片,而是在传输层进行分片,因此TCP通信的双方需要协商MSS值,当TCP层数据发现超过了MSS值时,就会进行分片,当然这里也会保证分片后的形成的IP数据包不会超过MTU的大小,这样IP层才不会进行分片。
SYN攻击
什么是SYN攻击
攻击方通过伪造不同的IP短时间内发送很多SYN报文,服务器端发送的ACK+SYN报文客户端不予回应,这样服务端会有很多很多个处于SYN_RCVD的连接,时间越久,半连接队列(SYN队列)里的数量多越来越多,直至无法为正常的客户端服务。
如何避免SYN攻击
# 当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。用于控制该队列的的最大值
net.core.netdev_max_backlog
# SYN_RCVD状态连接的最大个数
net.ipv4.tcp_max_syn_backlog
# 超出处理性能时,对新的SYN报文直接回复RST
net.ipv4.tcp_abort_on_overflow
# 开启tcp_syncookies
net.ipv4.tcp_syncookies=1
开启tcp_syncookies以后,当SYN队列满时,后续的SYN包不再进入队列,而是计算一个cookie值,然后发送ACK+SYN报文给客户端。
客户端在收到报文后也会回复ACK报文给服务端,服务器会检查这个ACK报文的合法性,如果合法就放入全连接队列(accept队列)。