TCP
面向连接,全双工,点对点。
TCP头格式
- TCP包没有IP地址,IP地址在网络层的IP协议中,TCP包包括源端口号,目标端口号
- 一个TCP连接需要四个元祖表明是同一连接(src_ip,src_port,dst_ip,dst_port)
- 非常重要的几个字段:
- sequnces number 包的序号,解决网络包乱序问题。
- acknoledgement number ACK,用于确认收到,解决不丢包的问题,ACK的值是“期望对方发的包的seq”
- Window 滑动窗口,用于解决控流
- TCP Flag, 用于操控TCP的状态机。
连接与断开
背下来下面这张图:
这张图很好的阐述了三次握手和四次挥手,握手三次挥手四次原因如下:
握手时,双方仅需要互相知道对方的ISN(也就是Inital Sequence Number),也就是上图中的SYN的seq,这个号要作为以后的数据通信序号,实际上第三次握手时已经可以携带数据。挥手可以理解为两个来回,发送方和接收方都需要FIN和ACK,对于被动方,在回复对方的断开连接请求后,需处理好自身状态,发送自己的断开请求。发送方收到被动方的断开请求后,回复并进入TIME_WAIT状态,该状态持续2MSL。
TCP的TIME_WAIT
设置为2MSL的原因是,如果被动方没收到ACK,就会重发FIN,一来一回刚好2MSL。此外,这样做有足够的时间让这个连接不会和后面的连接混在一起。TIME_WAIT如果数量太多,会比较消耗系统资源,可以更改tcp_tw_reuse和tcp_tw_recycle开关,但使用这两个开关要求双方必须打开tcp_timestamps,因此这些改变可能引发诡异问题,官方不建议。
对抗DDOS
对抗DDOS攻击,可以用tcp_max_tw_buckets来控制TIME_wait的数量,默认180000,超过限制后,系统会把多的TIME_WAIT destroy掉,然后日志打警告。
TCP的半连接
三次握手过程中,服务器发送SYN=1,ACK=1的包后,收到客户端的ACK包前的TCP连接为半连接。此时可能受到SYN攻击,即攻击客户端短时间内伪造大量不存在的IP地址,向服务器不断发送SYN包,服务器回复确认包并等待客户的确认,由于源地址不存在,服务器需要不断地重发至超时(Linux默认重试次数为5,重试间隔为1s,2s,4s,8s,16s,确定第五次超时需要32s,共计63s TCP才断开这个连接),这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,导致系统运行缓慢,网络堵塞,系统瘫痪。
TCP KeepAlive
TCP的连接是软件层概念非物理层概念,TCP双方并不是一直存在数据交互,而是使用KeepAlive机制实现。
TCP KeepAlive基本原理:隔一段时间发送一个探测包,若收到对方回应的ACK,则认为连接存活,若超过一定重试次数还是没有收到回应,则丢弃该TCP连接。
TCP的可靠性控制
接收方会返回最后一次接收正确的报文的+1序号(ACK为序号期望值)。如果发送方连续三次收到同一个ACK值,则说明从该发送报文后的报文已丢失,因而重传序号为该ACK值的包(该设计也称为快速重传)。接收方收到失序数据时,会将失序数据缓存(数量没办法超过滑动窗口的大小),收到序号为当前ACK的包后,ACK会变为下一期望包。例如,接收方没收到3,收到4和5,则缓存4和5,待3收到后,ACK直接设置为6。
TCP的拥塞控制
拥塞控制作用于网络,防止过多数据注入网络中,避免网络负载过大。其方案主要为慢开始,拥塞避免和快重传,快重传上面已经说过,不待超时,收到三个相同ACK则重传。下面讲一下慢开始和拥塞避免算法。
发送方维持一个叫拥塞窗口cwnd的状态变量,该窗口大小动态变化,发送方的发送窗口小于等于拥塞窗口。只考虑发送方单工传输,慢开始即cwnd大小从1开始,每次收到接收方的确认,则cwnd翻倍,直到cwnd>ssthresh(慢开始限制),此后,cwnd的变化改为使用拥塞避免算法。
拥塞避免算法中cwnd增长更加缓慢,每经过一个往返,cwnd就加1,线性缓慢增长。
无论慢开始阶段还是拥塞避免阶段,只要发送方判断出现网络阻塞,就把sshthresh设置为出现拥塞的发送窗口大小的一半(但不小于2),然后cwnd重置为1,重新开始慢开始算法,以迅速减少主机发到网络中的分组数,使拥塞的路由器有足够时间把队列中积压的分组处理完毕。