计算机网络(一)TCP

原文是皓叔的TCP的哪些事儿

TCP是传输层的协议,它是面向连接的。这里的连接指的只是状态上的连接(即维持了一系列状态,动态的改变状态),而不是实际连接起来的。

要了解TCP,就需要对这几部分进行理解:

  1. 协议的首部结构
  2. 重传机制
  3. 流量控制
  4. 拥塞控制
  5. 各种算法

一、协议的首部结构

在这里插入图片描述
首部的前20个字节是固定的
Source Port:源端口
Destination Port:目的端口。
  源IP,目的IP,源端口,目的端口,协议类型 可以唯一确定TCP连接。
Sequence Number:序号
  解决网络中 乱序 的问题
Acknowledgement Number:确认号
  可以理解为希望对方下次带来的包的序号,解决了丢包的问题,可靠传输。
TCP Flags:状态位
  用于描述TCP的状态。记住开头说的话。
Window:滑动窗口
  用于流量控制。
接下来的两个图:
第一个是TCP中的状态转换。
第二个是TCP建立连接、传送数据、释放连接的过程。

在这里插入图片描述
三次握手:

  1. Client主动打开,发送SYN包和客户端的序号x。由CLOSED状态变为SYN_SENT
  2. Server被动打开,由CLOSED变为LISTEN,监听请求。监听到SYN建立连接请求,则接收请求,并发送SYN+ACK包以及自己的序号y,ack=x+1,此时变为了SYN_RCVD状态。
  3. Client接收到SYN_ACK包之后,发送ACK包,ack=y+1,状态变为ESTABLISHED
  4. Server收到ACK包,状态变为ESTABLISHED,成功建立连接。

传输数据

四次挥手:

  1. Client 发送FIN包和自己的序号u,ack=v+1,表示告诉Server准备释放连接。状态变为FIN_WAIT_1
  2. Server 发送ACK包,ack=u+1,表示告诉Client我知道了,状态变为CLOSE_WAIT,Client接收到之后,状态变为FIN_WAIT_2
  3. 接着Server 发送FIN包,seq=v+1,表示告诉Client我也要准备释放连接了。状态变为LAST_ACK(最后确认)。
  4. Client接收到FIN包之后,发送ACK包,ack=v+2,状态变为TIME_WAIT
  5. Server接收到之后变为CLOSED状态,释放连接。
  6. Client等到TIME_WAIT时间过去之后,变为CLOSED状态,释放连接。

在这里插入图片描述

为什么要三次握手呢?

主要是为了双方都可以知道对方的序号,以防止乱序问题出现。
防止已失效的连接又出现,导致已经释放的连接,又开启。

为什么要四次握手呢?

因为TCP是全双工的,要确保对方知道,自己要关闭,且确认对方关闭。

TIME_WAIT是什么呢?

TIME_WAIT,是一段等待时间,是为了确保对方可以收到自己的ACK包。一般设置在2*MSL(最大报文存活时间)。Linux默认为30s。
有足够的时间让这个连接不会跟后面的连接混在一起(你要知道,有些自做主张的路由器会缓存IP数据包,如果连接被重用了,那么这些延迟收到的包就有可能会跟新连接混在一起)

还有几点需要注意的地方(这里建议在皓叔的博客中看,比较详细)

关于建立连接SYN超时

在三次握手中,Client向Server发起建立连接的请求,Server返回SYN+ACK之后,Client挂掉了,所以Server无法接受Client的ACK包,Server在一定时间内会进行重发SYN+ACK包。Linux中默认重发次数为5次,每次的间隔是1,2,4,8,16s,所以在发送完第5次之后仍无响应的话,就会断开,总共需要1+2+4+8+16+32=63s,32s是因为第4次和第5次间隔为16,你要确保第5次发送且没有收到ACK。

关于SYN Floor攻击

在等待的地方,总会存在着各种危险。 上述需要等待63s才会断开连接,这是非常消耗服务器资源的。如果攻击者制造SYN Floor攻击,在发送SYN包之后就下线了。会导致服务器的SYN队列耗尽,正常的请求不能处理。针对SYN队列耗尽,Linux提供了tcp_syncookies参数,当SYN队列满了之后,通过源端口、目的端口和时间戳,生成一个Sequence Number(cookie)发回去,如果是攻击者,则不会响应,如果是正常连接,则会响应。请注意,请先千万别用tcp_syncookies来处理正常的大负载的连接的情况。 对于正常的请求,你应该调整三个TCP参数可供你选择,第一个是:tcp_synack_retries 可以用他来减少重试次数;第二个是:tcp_max_syn_backlog,可以增大SYN连接数;第三个是:tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了

关于序号的初始化(ISN)

ISN是不能硬编码的,比如:如果连接建好后用1来做ISN,如果Client发了30个segment过去(还没有到),但是网络断了,于是Client重连又用1做ISN,但是之前连接的发送的包到了,于是就被当成了新连接发送的包,此时client的序号可能为3,而Server端认为Client端的这个号是30。
ISN会和一个假的时钟绑定在一起,这个时钟每4微秒对ISN做加一操作,直到超过2^32,又从0开始。这样一个ISN的周期大约是4.55个小时。所以只要MSL小于4.55,我们就不会重用ISN,不会发生上述问题。

关于TIME_WAIT数量太多

在大并发的短连接下,TIME_WAIT数量太多,会消耗很多系统资源。可以使用tcp_tw_reuse和tcp_tw_recycle,但是这两个参数不可以随便用,有可能导致很大的错误。

TCP重传机制

重传机制就是保证了TCP的可靠性。

超时重传机制(时间驱动)
发送端发送包,接收端会返回一个ack。
如果没有收到某个包的ack且超时以后,发送端就会重发这个包。
快速重传机制(数据驱动)
接收端收到1之后,返回ack=2,但是2在途中丢失了,收到了3,4,5,因为2还没有收到,所以在每次接收时会返回ack=2。接收端在连续收到三个ack=2的确认之后,发送端就知道2还没有到,于是重传2。
在这里插入图片描述
但是重传的时候应该重传ack=2的包、还是重传2345包呢?
第一种,重传ack=2,节省带宽,但是如果2345都丢了,会浪费时间。
第二种,重传2345,速度快,但是如果345都在,那么就是浪费带宽。

SACK
Selective Acknowledgement(SACK)。在TCP首部加一个SACK,汇报收到的数据。如下图:
在这里插入图片描述
Linux下,通过tcp_sack打开这个功能。

接收方Reneging,所谓Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了。这样干是不被鼓励的,因为这个事会把问题复杂化了,但是,接收方这么做可能会有些极端情况,比如要把内存给别的更重要的东西。所以,发送方也不能完全依赖SACK,还是要依赖ACK,并维护Time-Out,如果后续的ACK没有增长,那么还是要把SACK的东西重传,另外,接收端这边永远不能把SACK的包标记为Ack。

Duplicate SACK – 重复收到数据的问题
Duplicate SACK又称D-SACK,其主要使用了SACK来告诉发送方有哪些数据被重复接收了。

未完待续,接下来会学习一下TCP中的算法,然后总结在这里。

猜你喜欢

转载自blog.csdn.net/qq_39513105/article/details/89296947