TCP基本认识
什么是TCP
-
TCP头格式有哪些
-
序列号:每个TCP都有一个标识,通过SYN包传给接收端,每发送一次数据,就累加数值,目的是防止TCP在网络中乱序。
-
确认应答号:确认应答号表示下一个分配的序列号,接收端主机收到确认应答号,表示收到该应答号之前的网络包。目的是解决TCP包在网络中丢失的问题。
-
控制位
ACK:ACK为1,表示收到之前的消息,确认应答位有效。
RST:RST为1,表示发生异常,连接强制中断。
SYN:SYN为1,表示建立连接,序列化进行初始化。
FIN:FIN为1,表示希望关闭连接,之后不会有数据发送。
-
-
为什么要用TCP
TCP工作在传输层,主要解决包在网络中可靠传输的问题(无损、有序)。
-
什么是TCP/TCP连接呢?
TCP有三个关键词:面向连接、可靠传输、字符流。
-
面向连接是指TCP必须在两端之间建立一对一连接才可以通信。
-
可靠传输是指TCP在网络中不会丢失,一定会到达另一方。
-
字符流是指TCP会把报文拆分,通过消息的边界来判断有效的用户消息。
TCP连接是指客户端和服务端达成三个信息的共识,就叫建立了连接。
- Socket:有IP地址和端口号组成
- 序列号:解决乱序问题。
- 窗口大小:流量控制。
-
-
确认一个唯一的TCP连接
TCP四元组可以确认一个唯一的TCP连接【目的端口、目的地址;源地址、源端口】
其中源地址、目的地址在IP头部,用来确认报文发送给网络中的哪个主机。
而源端口、目的端口在TCP头部,用来确认报文发送给主机中的哪个进程。
最大TCP连接数 = IP数 x 端口数,当然,这只是理论值,实际还受两个方面的影响:
- 文件描述符:TCP连接本质是一个文件,受文件描述符上限的控制
- 内存限制:每个TCP连接都要占用一定内存。
TCP/UDP
-
UDP和TCP有什么区别?应用场景?
UDP是不可靠传输,并且是无连接的,也就是不依靠连接来传输数据,不止可以进行一对一,还可以一对多发送数据。
UDP的头部没有首部字段,因为UDP的首部大小是固定的,而TCP因为还有选项字段,首部大小不固定。
分片不同
- TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
- UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。
应用场景:UDP:DNS、视频等媒体;TCP:HTTP/HTTPS、文件传输。
-
TCP和UDP的端口可以使用同一个吗?
可以使用一个,端口只不过是用来表示同一个主机中的不同进程的。传输层的端口号,用来表示主机上的不同程序的数据包。
当主机收到数据包后,可以在 IP 包头的「协议号」字段知道该数据包是 TCP/UDP,所以可以根据这个信息确定送给哪个模块(TCP/UDP)处理,送给 TCP/UDP 模块的报文根据「端口号」确定送给哪个应用程序处理。
TCP连接建立
TCP 的连接状态查看,在 Linux 可以通过 netstat -napt
命令查看。
三次握手
-
三次握手的过程
首先,TCP三次握手是为了保证客户端和服务器之间的连接是可靠的,并且可以传输数据。
第一次握手(SYN):客户端向服务端发送SYN(同步)标志的TCP数据包,并指定客户端的初始化序列号(ISN)。
第二次握手(SYN + ACK):服务端收到客户端发送的包后,会向客户端发送一个带有SYN + ACK(确认)标志的数据包,同时指定服务器的初始化序列号(ISN)。
第三次握手(ACK):客户端收到服务器发送的包后,向服务器发送带有ACK标志的包作为响应,表示客户端已经收到了服务器的响应,建立连接成功。
-
为什么是“三次”
-
首要原因是可以阻止旧的重复连接初始化造成混乱。
如果客户端发送SYN报文(10),网络阻塞,客户端又发送一个SYN报文(20),此时SYN为10的报文到达。
服务端返回ACK,报文确认序列号为11,不是客户端想要的连接(想要21),客户端就发送RST中止连接。这样就防止了旧的连接初始化。
- 如果是两次握手,服务端返回ACK后就建立了连接,发送了数据,就浪费了资源。
-
可以同步双方初始序列号。
通过初始序列号,就可以知道发送出去的信息,哪些已经被对方收到。就要通过三次的一来一回,才能确保双方的初始化序列号被同步。
而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。
-
避免资源浪费。
如果SYN报文阻塞,客户端重复发送多次SYN报文,那么服务端就会在收到请求后建立多个冗余的无效连接,造成资源浪费。
-
连接建立
-
初始化序列号为什么要随机
如果每次建立连接,客户端和服务器的初始化序列号都一样,很容易出现历史报文被下一个TCP连接接收的问题。
-
怎么随机序列号
RFC793 提到初始化序列号 ISN 随机生成算法:ISN = M + F(localhost, localport, remotehost, remoteport)。与计时器还有Hash算法有关,
由于随机数是会基于时钟计时器递增的,基本不可能会随机成一样的初始化序列号。
-
IP会分片,为什么需要MSS?
MTU:网络包的最大长度,一般为1500字节。
MSS:除去IP和TCP头部,一个网络包能容纳的最大TCP数据长度。
如果IP层有超过MTU大小的数据,IP层就会进行分片,来保证每一片的长度小于MTU。
如果把TCP的分片交给IP,如果一个IP分片丢失,整个IP报文的所有分片都要重传。所以为了达到最佳的传输效率,TCP协议在建立连接时,通常要协商双方的MSS值,当 TCP 层发现数据超过 MSS 时,则就先会进行分片,当然由它形成的 IP 包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。
经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。
握手丢失
-
第一次握手(SYN)丢失
客户端迟迟收不到ACK,触发超时重传机制,而且重传的SYN报文的序列号是一样的。
在 Linux 里,客户端的 SYN 报文最大重传次数由
tcp_syn_retries
内核参数控制,这个参数是可以自定义的,默认值一般是 5。一般超时重传的等待时间为上一次超时时间的二倍。
-
第二次握手(SYN+ ACK)丢失
可能会让客户端以为自己的SYN丢失,并且服务端认为自己的SYN + ACK丢失,服务端和客户端都会重传
- 客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由
tcp_syn_retries
内核参数决定; - 服务端会重传 SYN-ACK 报文,也就是第二次握手,最大重传次数由
tcp_synack_retries
内核参数决定。
- 客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由
-
第三次握手(ACK)丢失
ACK报文不会重传,ACK报文丢失,服务端重传对应的SYN报文。
SYN攻击
-
什么是SYN攻击?如何避免?
-
SYN攻击原理:
攻击者伪造不同IP地址的SYN报文请求连接,服务端收到连接请求后分配资源,回复
ACK+SYN包,但是由于IP地址是伪造的,无法收到回应,久而久之造成服务端半连接队列被占满,无法正常工作。
-
预防SYN攻击的方法:
-
增大半连接队列
要想增大半连接队列,我们得知不能只单纯增大 tcp_max_syn_backlog 的值,还需一同增大 somaxconn 和 backlog,也就是增大全连接队列
tcp_max_syn_backlog
(最大TCPSYN队列积压):处于SYN_RECV的TCP最大连接数。 -
开启tcp_syncookies功能
开启 syncookies 功能就可以在不使用 SYN 半连接队列的情况下成功建立连接。
当半连接队列满了,开启这个功能可以让后续连接不进入半连接队列,而是计算一个cookie,作为请求报文序列号发给客户端,如果服务器端收到客户端确认报文,会检查ack包合法性,如果合法,直接加入到accept队列。
-
减少SYN + ACK 重传次数
当服务端受到 SYN 攻击时,就会有大量处于 SYN_RECV 状态的 TCP 连接,处于这个状态的 TCP 会重传 SYN+ACK ,当重传超过次数达到上限后,就会断开连接。
那么针对 SYN 攻击的场景,我们可以减少 SYN+ACK 的重传次数,以加快处于 SYN_RECV 状态的 TCP 连接断开。
SYN-ACK 报文的最大重传次数由
tcp_synack_retries
内核参数决定(默认值是 5 次),比如将 tcp_synack_retries 减少到 2 次
-
-