TCP简介
TCP,Transmission Control Protocol,传输控制协议。
TCP是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。即面向连接、可靠、基于字节流的传输层通信协议。
TCP协议位于OSI七层模型中的传输层,为下层提供可靠传输。
数据链路层负责数据交换、网络层负责路径选择、应用层负责业务逻辑,而TCP可以在传输层提供可靠的传输。
TCP头部格式
源端口:16位,2字节。
目的端口:16位,2字节。
序号:此报文的序号,用于标识报文和被确认。
确认号:用于确认序号。
数据偏移:
- 意思是数据部分往后偏移
- 这是TCP的头部长度字段,代表有多少个32bit,即1代表32bit,2代表64bit。
- 一共4位最大为15,15×32=480b=60B。所以头部最大为60个字节,最小为20字节。
- 此字段与图中选项字段相关联。
保留字段:未使用。
通信字段:
- URG(紧急指针,应加速传送)
- ACK(用于确认)
- PSH(紧急位,应立即传送)
- RST(复位,比如拒绝连接)
- SYN(请求连接)
- FIN(请求断开连接)
这6位用于TCP通信,相应位置1表示特殊的信号。
比如典型的SYN、FIN用于三次握手和四次挥手。
窗口:用于拥塞控制,接收方可以控制发送方发送数据报文的吞吐量,防止自己太忙来不及接收。
校验和:此字段用来校验数据是否出错。
紧急指针:用于发送紧急数据的情况。
选项与填充:
- TCP头部的长度可以变化,因此可以承载更多的数据。
注意这里必须使用32位的数据,这样就可以快速定位数据部分的位置,所以没有那么多数据的话需要在后面进行填充。
这里与数据偏移字段相关联,这样就可以知道头部的长度以及数据部分的准确位置。
TCP的实现逻辑
为满足TCP协议的这些特点,TCP协议做了如下的规定:
1.到达确认
接收端接收到报文时,根据报文序号向发送端发送一个确认;
- 确认号=需要确认的报文的序列号Seq+需要确认的报文的数据长度Len
三次握手时:
-
客户端向服务器发送一个同步数据包请求建立连接,该数据包中,初始序列号(ISN)是客户端随机产生的一个值,确认号是0;
-
服务器收到这个同步请求数据包后,会对客户端进行一个同步确认。这个数据包中,序列号(ISN)是服务器随机产生的一个值,确认号是客户端的初始序列号+1;
-
客户端收到这个同步确认数据包后,再对服务器进行一个确认。该数据包中,序列号是第2步中,也就是同步请求确认数据包中的确认号值,确认号是服务器的初始序列号+1。
传输数据时:
-
初始序列号随机产生,后续序列号为上一个数据包(对方发送的数据包)的确认号。
-
确认号为上一个对方发送过来的数据包中的序列号+长度Len
-
如果连续发送多个数据包:确认号同样是2中的ACK号,不变;
2.超时重传
发送方在发送报文后启动超时定时器,如果在定时器超时之后没有收到相应的确认ACK,重发报文;
3.重复处理
作为IP数据报来传输的TCP报文会发生重复,TCP的接收端必须丢弃重复的数据;
4.数据校验
TCP将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验和有差错,TCP将丢弃这个报文,并不确认收到此报文段导致对端超时并重发。
5.滑动窗口
TCP连接每一方的接收缓冲空间大小都固定,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP在滑动窗口的基础上提供流量控制,防止较快主机致使较慢主机的缓冲区溢出。
滑动窗口是指可发送报文的数量,比如上图是51-31=20。
只有在窗口中的报文才允许发送。
收到35的ACK后,就认为之前的报文也已收到。(发送一个ACK就行了)
收到35的ACK后,滑动窗口向前滑动到36-55,多出来的52-55=4个报文可以发送给对方。
这样的机制可以防止缓冲区溢出。
因为未确认的话,窗口就不会滑动,就不会发送新的报文。
6.拥塞控制
发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。
当cwnd < ssthresh时,使用慢开始算法。
当cwnd ≥ ssthresh时,改用拥塞避免算法。
ssthresh初始值为16,后期ssthresh=max( cwnd/2 , 2 )
慢启动阶段
cwnd初始值为1,收到确认ACK后,按指数增长增加cwnd。
拥塞避免阶段
收到确认ACK后,swnd+1。
7.快速重传
重传存在的问题:
当一个报文段丢失时,会等待一定的超时周期然后才重传分组,增加了端到端的时延。
当一个报文段丢失时,在其等待超时的过程中,可能会出现这种情况:其后的报文段已经被接收端接收但却迟迟得不到确认,发送端会认为也丢失了,从而引起不必要的重传,既浪费资源也浪费时间。
快重传算法规定:
发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
为什么是三次ACK?
首先要明白一点,即使发送端是按序发送,由于TCP包是封装在IP包内,IP包在传输时乱序,意味着TCP包到达接收端也是乱序的,乱序的话也会造成接收端发送冗余ACK。那发送冗余ACK是由于乱序造成的还是包丢失造成的,这里便需要好好权衡一番,因为把3次冗余ACK作为判定丢失的准则其本身就是估计值。
8.快速恢复
当发送端收到连续三个重复的确认时,由于发送发现在认为网络很可能没有发生拥塞,因此现在不执行慢开始算法而是执行快恢复算法。
- 慢开始门限 ssthresh = 当前拥塞窗口 / 2
- 新拥塞窗口 cwnd = 慢开始门限 ssthresh
- 开始执行拥塞避免算法,是拥塞窗口缓慢地线性增大
TCP协议过程
1.三次握手
建立连接有三步:
1.Client发送SYN至Server
2.Server收到SYN之后,对此SYN进行回应:发送包含ACK和SYN的数据包至Client。
3.Client也对此SYN进行回应,发送ACK至Server。
2.四次挥手
释放连接有四步:
1.客户端发送FIN至Server。
2.Server对此FIN进行回应:发送ACK给Client。
3.Server也请求关闭连接,发送FIN给Client。(这时FIN和ACK分开,而三次握手中SYN和ACK为同一个包)
4.Client对FIN进行回应:发送ACK至Server。
3.seq和ack设置原则
起初seq为随机值。之后发送的seq为上一个发送的数据包seq值+1
ack对上一个收到的数据包进行回应,为上一个收到的数据包seq值+1。
若连续发送多个包,则ack不变,依然为上一个收到的数据包seq值+1。seq每一次都自增。
Linux中的TCP
1.netstat命令
netstat
-t #显示TCP的连接情况
-u #显示UDP的连接情况
-a #显示所有连接情况
-l #只显示Listening状态的连接
-n #显示IP而不是域名
-p #显示PID和程序名
2.nc命令
举例
监听
nc -l 10086 #监听TCP端口10086
nc -ul 10086 #监听UDP端口10086
扫描
nc -zv 192.168.191.128 10088
参数
监听
-l #监听端口(默认监听TCP)
-u #配合-l监听UDP端口
发送数据
-z #扫描端口,但不发送任何信息
-s #指定发送数据的源地址
-v #输出调试信息
-w #超时时间(秒)
echo hello > /dev/tcp/192.168.191.128/10086 #使用TCP发送数据到192.168.191.128:10086
exec 8>/dev/tcp/192.168.191.128/10086 #监听TCP的10086端口(接收数据e后TCP不断开)
3.网络优化
vim /etc/sysctl.conf
net.ipv4.tcp_syn_retries=2 #最多发起2次SYN请求(默认为5)
net.ipv4.tcp_fin_timeout=30 #FIN_WAIT_2时间
net.ipv4.tcp_max_syn_backlog = 4096 #SYN队列长度(默认为1024),增加以容纳更多等待连接数
net.ipv4.tcp_tw_reuse = 1 #开启TIME-WAIT sockets重用机制(默认为0,即关闭),可重新用于新的TCP连接
net.ipv4.tcp_tw_recycle = 1 #开启TIME-WAIT sockets快速回收(默认为0,即关闭)
net.ipv4.tcp_syncookies = 1 #当SYN等待队列溢出时,启用cookies来处理(默认为0,即关闭),可以防范少量SYN攻击
net.core.netdev_max_backlog=3000 #每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.ipv4.ip_forward = 1 #开启内核转发
sysctl -p #查看内核参数