TCP(1)---三次握手及四次挥手

TCP(Transmission Control Protocol)

主要特点:
  • 面向连接运输层协议,也就是说程序使用TCP前需建立TCP连接,传输完毕后需释放连接,也就是说应用程序类似 “接电话”,但也要 “挂电话”。

  • 每个TCP连接仅两个端点,每条TCP连接只能点对点传输。

  • TCP提供全双工通信。TCP允许通信双方应用进程任何时候都可发数据。TCP连接的两端都设有发送缓存接收缓存,用来临时存放双向通信的数据,应用程序把数据传送给TCP的缓存后,即可做自己的事去,而TCP则在适合的时候把数据发送出去,接收时,TCP把收到的数据放到缓存中,上层应用进程在合适时候读取缓存。

  • 面向字节流,TCP的 “流”指的是流入到进程或从进程流出的字节序列

TCP三次握手过程描述:

TCP三次握手过程

注意:上图第三次握手ack = y + 1(原图画错了)


Server端的SYN队列


半连接队列:

在三次握手协议中,服务器维护一个半连接队列,该队列为每个客户端的SYN包开设一个条目(服务器端在接收到SYN包时,就已经创建了request_sock结构,存储在半连接队列中),该条目表明所识别的连接在服务器处于SYN_RCVD状态,当服务器收到客户端的确认包时,删除该条目,服务器进入ESTABLISHED状态,未连接队列的大小为max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog),也就是可以在说未连接队列的大小可以在/proc/sys/net/ipv4/tcp_max_syn_backlog中修改配置,如果服务器经常出现过载,可尝试增加这个数字(tcp_max_syn_backlog)。

半连接(half-open connect)存活时间:

是指半连接队列的条目存活的最大时间,也即服务器端从收到SYN包确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和,有时我们也称半连接存活时间为Timeout时间、SYN_RCVD存活时间。


Server端的ACCEPT队列

完全连接队列:

在第三次握手时,当Server接收到ACK报之后,会进入一个新的叫ACCEPT的队列,该队列的长度为min(backlog, /proc/sys/net/core/somaxconn),默认情况下,somaxconn的值为128(我的centos上是128),表示最多有129个ESTABLISHED的连接等待accept(),而backlog值则是由int listen(int sockfd, int backlog)中的第二个参数指定(指定的backlog与半连接状态的backlog无关系),listen里面的backlog可以由我们的程序去定义。

PS:当服务器绑定、监听某个端口后,这个端口的SYN队列和ACCEPT队列就建立好了。

半连接队列未满但是全连接队列已满:

客户端发出SYN分节,服务器端收下SYN分节并向客户端发送SYN+ACK,客户端收到服务器端SYN+ACK后,成为ESTABLISHED状态,并向服务器端发送第三次握手ACK,服务器端收到ACK后发现全连接队列已满,默认情况下服务器端什么也不做,状态依然是SYN_RCVD,此时ListenOverflows+1,同时服务器端通过对目录:/proc/sys/net/ipv4/tcp_abort_on_overflow进行修改来决定如何返回,0表示直接丢弃,1表示发送RST通知客户端(ListenOverflows默认为0,当全连接队列超过上限时,ListenOverflow+1),客户端会重传SYN(客户端第一次握手发起的请求)和ACK(客户端第三次握手期间发出的确认),并且内核会限制SYN队列的处理速度,如果在SYN队列中收到太多的SYN,服务器端将会丢弃一些,这样,丢弃的SYN对应的客户端需要重发SUN包,当达到一定的阈(yù)值(可以理解为连接被动打开方的确认连接的应答最大重试数,即对于一个新建连接,内核需要发送多少SYN连接请求才决定放弃,阈值可以/proc/sys/net/ipv4/tcp_synack_retries中修改),客户端与服务器断开连接,服务器删除客户端在半连接队列中的SYN分节。

不论全连接满没满,若半连接队列已满:

不开启tcp_syncookies的时候,服务器端会丢弃新来的SYN包,而客户端多次重发SYN包得不到响应而返回超时错误(connection time out)。但是当服务器端开启了tcp_syncookies = 1,那么SYN半连接队列就没有逻辑上的最大值了,并且/proc/sys/net/ipv4/tcp_max_syn_backlog设置的值也会被忽略。

TCP连接过程中的参数:
  • tcp_max_syn_backlog:(/proc/sys/net/ipv4/tcp_max_syn_backlog)
    半连接队列的长度,默认是1024

  • tcp_syn_retries:(/proc/sys/net/ipv4/tcp_syn_backlog)
    第一次握手时,客户端会给服务器发送SYN,如果服务器端未返回SYN-ACK报文,那么客户端会重发SYN,重发次数即为tcp_syn_retries,默认值为5次(不应该大于255),超过五次服务器端还未能发送SYN-ACK报文,则本次连接失败。服务器未返回SYN-ACK有两种情况:一种是网络问题,另一种是服务器SYN队列已满,导致SYN包被丢弃。

  • tcp_synack_retries:(/proc/sys/net/ipv4/tcp_synack_backlog)
    决定了内核在放弃连接之前发送SYN-ACK包的数量,即当无法收到客户端第三次握手的ACK时,服务器端会重发SYN-ACK包,重发的次数即为tcp_synack_retries,默认值为5次(不应该大于255),超过五次还未收到客户端确认,则本次连接失败。客户端未返回ACK有两种情况:一种是客户端在接收到服务器端的SYN-ACK后,还未发送ACK时客户端就掉线了(客户端ACK超时),另一种是全连接队列已满,客户端重发无法被确认(全连接队列已满时客户端重发ACK不属于客户端ACK超时)。

  • tcp_syncookies:(/proc/sys/net/ipv4/tcp_syncookies)
    防止SYN Flood攻击。

  • tcp_abort_on_overflow:(/proc/sys/net/ipv4/tcp_abort_on_overflow)
    ACCEPT队列已满处理不过来时,设置了该参数,让其由0变为1,然后内核会回发RST包。

服务器SYN超时:

当客户端发送SYN时,服务器未能返回SYN-ACK报文,那么客户端重发,重发次数由tcp_syn_retries参数设置,该值默认为5,然后五次重发后服务器依旧未能响应客户端,这称为服务器SYN超时,超时原因主要有两种情况(上面讲tcp_syn_retries时说过):一种即就是网络问题,另一种是服务器SYN队列已满,导致SYN包被丢弃。

客户端ACK超时:

如果服务端接到了客户端发的SYN并回发SYN+ACK后客户端掉线了,这时服务端没有收到客户端回来的ACK,那么,这个连接处于一个中间状态,既没成功也没失败。于是,服务端端如果在一定时间内没有收到客户端端的ACK,那么服务端端会重发SYN+ACK。在Linux下,默认重试次数为5次,重发的间隔时间从1s开始每次都翻番(指数退避),重传间隔分别为1,2,4,8,16,32等等,默认重传是五次,如果第五次重传还未成功,所花费的时间就是1 + 2 + 4 + 8 + 16 + 32 = 63秒。


SYN攻击:

SYN攻击的原理就是向服务器发送SYN数据包,并伪造源IP地址,服务器在收到SYN数据包时,内核会为这个连接信息分配核心的内存并放入半连接队列,并向源IP发送SYN-ACK数据包,并等待ACK数据包以完成三次握手建立连接。由于源IP地址是伪造的不存在的主机IP,所以服务器无法收到ACK数据包,并会不断重发,同时半连接队列被不断攻击的SYN数据包占满,导致无法处理正常的连接。

SYN攻击处理:

针对SYN攻击的几个环节,提出相应的处理方法:

  1. 减少SYN-ACK数据包的重发次数(默认是5)
    sysctl -w net.ipv4.tcp_synack_retries=3
    sysctl -w net.ipv4.tcp_syn_retries=1

  2. 使用SYN cookie技术:
    sysctl -w net.ipv4.tcp_syncookies=1

  3. 增加半连接队列(默认为1024)
    sysctl -w net.ipv4.tcp_max_syn_backlog=2048

  4. 限制SYN并发数:
    iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT --limit 1/s

    可用hping工具模拟SYN攻击,还有synkill也可以用来模拟SYN攻击。


TCP四次挥手过程描述:

TCP四次挥手过程

一个TCP连接收到FIN后仍能发送数据,首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭
1)客户端发送一个FIN,即数据seq=x,用来关闭客户端到服务器的数据传送。
2)服务器收到这个FIN,回发ACK,并且确认序列号:ack=x+1
3)服务器关闭与客户端的连接,发送FIN给客户端。
4)客户端发回ACK报文确认,确认序列号为ack=z+1(即收到的序列号+1)。

TCP四次挥手各个状态分析:

FIN_WAIT_1:
这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待服务器端(被动方)的FIN报文,而这两种状态的区别是:FIN_WAIT_1状态实际上是当socket在ESTABLISHED状态时,客户端(主动方)想要主动关闭连接,向服务器端发送了FIN报文,此时该socket即进入到FIN_WAIT_1状态,而当服务器端回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论客户端是何种情况,服务器端都应该马上回ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

FIN_WAIT_2:
上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的socket表示半连接,也即有一方要求close连接,但另外还告诉对方“我暂时还有点数据需要传送给你,稍后再关闭连接。

LAST_ACK:
这种状态是被动关闭的一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

CLOSE_WAIT:
这种状态下即当客户端close一个socket后发送FIN报文给服务器端,接下来服务器端毫无疑问地回应ACK报文,此时则进入CLOSE_WAIT状态,接下来服务器需要考虑是否还有数据需要给客户端发送,如果没有,那么服务器也可以close这个socket,于是发送FIN报文告知客户端,”我的数据已经发送完毕了,可以关闭连接“,所以服务器在CLOSE_WAIT状态下需要完成的事情是等待服务器关闭连接

CLOSING:
这种状态比较特殊,实际情况中应该很少见,属于一种比较罕见的例外状态,正常情况下,当客户端发送FIN报文后,按理说应该先收到(或同时收到)服务器端的ACK报文,在收到服务器端的ACK报文,但是CLOSING状态表示客户端发送FIN报文后,并没有收到服务器端的ACK报文,反而却也收到服务器端的FIN报文,那么也就是说CLOSING状态实际上就是双方几乎同时发送FIN报文这种情况,也表示双方都正在关闭连接。

TIME_WAIT:
TCP是全双工的,所以需要等待所有分组(定义这个连接的套接字socket,双方的IP及端口)死掉,即确保该连接的所有分组全部消失,以防止出现确认丢失的情况,当定时器超时后,TCP删除客户端连接记录,返回到初始状态(CLOSED)。


为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

要建立三次握手的原因:

如果客户端的第一个请求发送后没有丢失,而是在网络节点中滞留,以至延误到TCP连接释放后的某个时段才到达服务器端,这个时候如果说服务器端接收了客户端的SYN,并且回发ACK报文确认,如果不采用 “三次握手”,那么此时此刻实际上已经建立了连接,然而刚也说到客户端的SYN请求延误到了TCP连接释放,那么此时尽管客户端与服务器端建立了连接,但是客户端不会理睬服务器端,所以服务器端就一直 “傻”等客户端发送消息,那么服务器端的资源则会浪费。

要建立四次挥手的原因:

当关闭连接时,服务器端收到了客户端的FIN报文通知,这仅仅表示客户端没有数据发送给服务器端了,我们知道TCP是全双工通信,所以未必服务器端的全部数据发送给了客户端,所以服务器端未必会马上关闭socket,也即服务器端还需要发送一些数据给客户端之后,再发送FIN报文给客户端,表示同意现在关闭连接,所以服务器端的ACK报文和FIN报文大多数情况下都是分开发送的。


为什么TIME_WAIT状态需要经过2MSL(Maximum Segment Lifetime 最大报文段生存时间)才能返回到CLOSE状态?

按道理四个报文都发送完毕,客户端和服务器端直接进入CLOSE状态,但我们必须假想网络是不可靠的,有可能最后一个客户端ACK丢失,这时候如果客户端(主动方)在TIME_WAIT状态,则还会再发送依次ACK,从而保证TCP可靠性,那么从这个解释来说,2MSL的时长设定是可以理解的,MSL是报文最大生存时间,如果服务器(被动方)重新发送一个FIN,客户端(主动方)重新ACK,再加上不定期的延迟时间,大致在2MSL的范围。


TTL字段:

TTL是网络数据包为了防止数据包在网络中无限循环,而设定的网络数据包在网络传输中的最大的转发次数,因为每转发一次在路由器,就会转向下一跳,通常称为最大跳数。

具体含义即就是:我们本地机器发出一个数据包,数据包经一定数量路由器后传送到目的主机,但由于多种原因,一些数据包不能正常传送到目的主机,那如果不给这些数据包一个生存时间的话,这些数据包就会在网络上不断的传送,导致网络开销的增大。当数据包传送到一个路由器之后,TTL就自动减1,如果减到0了还没有传送到目的主机,那么数据就会自动消失,发送数据的一方则请求超时。

默认情况下:
1. Linux中TTL为64或255
2. Unix主机的TTL为255
3. Win NT/2000/XP中TTL为128

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值