原文:http://my.oschina.net/guol/blog/48543
TCP/IP 协议簇中TCP提供可靠的服务连接,采用三次握手建立一个连接,如下图所示:
在客户端A和服务端B之间建立一条tcp连接,主要经过一下三步:
一 客户端 A发起建立连接的请求,向服务端B发一个SYN(seq=x)包。
二 服务端B收到SYN(seq=x)包后,必须对客户端发来的SYN(seq=x)包进行确认,向客户端A发送一个ACK(ack=x+1)包,同时自己也要发一个SYN(seq=y)包,这样等于服务端B就发了一个SYN+ACK的包。
三 客户端A收到服务端B发的SYN+ACK的包之后,必须向服务端B发送一个确认包ACK(ack=y+1),至此发包完毕,客户端进入链接状态,三次握手完毕。
在三次握手中客户端和服务端的状态变化如下:
客户端A 服务端B
A未发请求前 LISTEN
(在A未发请求前,B监听某个特定的服务端口)
A发送SYN SYN_SENT SYN_RECV
(A发送SYN后,B成功收到SYN后,状态变为 SYN_RECV,并且给A 发送ACK+SYN包)
A发送ACK ESTABLISHED ESTABLISHED
(A收到ACK+SYN后,给B发送ACK,B收到A发送的ACK后变为ESTABLISHED状态)
下面说说会影响tcp建立连接三次握手的一些因素:
1 /proc/sys/net/ipv4/tcp_max_syn_backlog (1024)
该变量控制每个监听端口接收的客户端发送的SYN队列的长度,输入的SYN报文段连接请求需要排队,直到本地服务端接收,如果连接数多于默认值,则新来的连接请求会被丢弃,在服务端会维护一个未连接队列,该队列为每个客户端发送的SYN包开设一个条目,说明已经收到SYN包,并且向客户端发出SYN+ACK包,等待客户端的确认SYN包,这时服务端出于SYN_RECV状态,如果tcp_max_syn_backlog过小,一直收不到客户端最后发来的SYN确认包,服务端就会一直出于SYN_RECV状态,查看netstat -an的时候如果SYN_RECV过多有可能就是tcp_max_syn_backlog过小。
2 /proc/sys/net/ipv4/tcp_synack_retries (5)
该变量控制内核向某个输入的SYN/ACK段重新发送响应的次数,降低取值可以更早的检测到客户端连接失败的尝试。
3 /proc/sys/net/ipv4/tcp_retries2 (15)
该变量控制内核向已经建立连接的远程主机重新发送数据的次数,降低取值可以更早的检测到与远程主机的连接失效,从而可以快速释放该链接的资源。
4 /proc/sys/net/ipv4/ip_local_port_range (32768 61000)
该变量控制系统上可用的临时端口的范围。端口是ip协议对各个socket加以区分的地址的逻辑抽象。
5 /proc/sys/net/ipv4/tcp_retries1 (3)
该变量设置放弃回应一个tcp连接请求前,需要进行多少次重试。
6 /proc/sys/net/ipv4/tcp_syncookies (0 关闭 1 打开)
该参数主要用来防止SYN FLOOD攻击。SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。
SYN Flood攻击是一种典型的拒绝服务型(Denial of Service)攻击。所谓拒绝服务型攻击就是通过进行攻击,使受害主机或网络不能够良好的提供服务,从而间接达到攻击的目的。
SYN Flood攻击利用的是IPv4中TCP协议的三次握手(Three-Way Handshake)过程进行的攻击。大家知道协议规定,如果一端想向另一端发起TCP连接,它需要首先发送TCP SYN 包到对方,对方收到后发送一个TCP SYN+ACK包回来,发起方再发送TCP ACK包回去,这样三次握手就结束了。我们把TCP连接的发起方叫作"TCP客户机(TCP Client)",TCP连接的接收方叫作"TCP服务器(TCP Server)"。值得注意的是在TCP服务器收到TCP SYN request包时,在发送TCP SYN+ACK包回TCP客户机前,TCP服务器要先分配好一个数据区专门服务于这个即将形成的TCP连接。一般把收到SYN包而还未收到ACK包时的连接状态成为半开连接(Half-open Connection)。
在最常见的SYN Flood攻击中,攻击者在短时间内发送大量的TCP SYN包给受害者,这时攻击者是TCP客户机,受害者是TCP服务器。根据上面的描述,受害者会为每个TCP SYN包分配一个特定的数据区,只要这些SYN包具有不同的源地址(这一点对于攻击者来说是很容易伪造的)。这将给TCP服务器系统造成很大的系统负担,最终导致系统不能正常工作。
TCP三次握手建立连接后,并没有规定必须立即进行数据交换,连接可以永久保持空闲状态,如果客户端发生故障,则将无法被其他主机检测到,而tcp的连接保持机制允许服务端对连接加以监视并在合理的时间内获知这种故障,影响参数有下:
1 /proc/sys/net/ipv4/tcp_keepalive_time (7200)
如果在该参数指定的秒数内连接始终处于空闲状态,则内核向客户端发起对该主机的探测
2 /proc/sys/net/ipv4/tcp_keepalive_intvl (75)
该参数以秒为单位,规定内核向远程主机发送探测指针的时间间隔
3 /proc/sys/net/ipv4/tcp_keepalive_probes (9)
该参数规定内核为了检测远程主机的存活而发送的探测指针的数量,如果探测指针的数量已经使用完毕仍旧没有得到客户端的响应,即断定客户端不可达,关闭与该客户端的连接,释放相关资源。
以上三个参数 7500+9*75 导致一条失效的连接在经过2小时11分钟后才被丢弃,降低上述数值可以减少失效连接所占资源的时间。
TCP断开连接的四次握手,过程如下:
初始时客户端和服务端都处于ESTABLISHED状态。
1 客户端先发送一个FIN包,告诉服务端他要断开连接。
2 服务端收到FIN包后,给客户端发送一个确认包ACK,告诉应用程序他要关闭连接了。
3 当应用程序准备好断开后,服务端给客户端发送FIN包。
4 客户端收到服务端发来的FIN包后,给服务端发送ACK确认包。服务端收到之后就关闭连接。
四次握手断开连接状态分析:
1 当服务端收到客户端发来的FIN包后,给客户端发送一个ACK确认包,然后通知应用程序准备关闭,因为应用程序可能还有些数据需要发送给客户端,所以这时服务端是CLOSE_WAIT状态,而不是CLOSED状态。
2 客户端发送FIN包要求断开连接后就进入了FIN-WAIT-1状态,当收到服务端发送的ACK包后就进入到FIN-WAIT-2状态,而在实际情况下FIN-WAIT-1比较难见,因为客户端发送FIN包后服务端会立刻发送ACK包。
3 客户端进入FIN-WAIT-2状态时,表示是半连接,即一方要求关闭连接后,另一方回复说我还有点数据要传输,稍后再关闭,这就是服务端发送ACK之后不立即进入CLOSED状态,而是通知应用程序准备关闭。
4 服务端应用程序准备好之后,内核发送FIN给客户端,等待客户端的ACK包,服务端进入LAST_ACK状态,这时候客户端收到FIN后,发送ACK报文给服务端,客户端进入TIME-WAIT状态,TIME-WAIT等待2MSL后进入CLOSED状态,服务端收到客户端发送的ACK包之后进入CLOSED状态。
2MSL时间,当客户端最后给服务端发送ACK后,不立即进入CLOSED状态,这2MSL时间是为了防止客户端发送的ACK包丢失而让服务端一直处于LAST_ACK状态,让服务端处于LAST_ACK超时重发FIN。
影响断开连接的参数:
1 /proc/sys/net/ipv4/tcp_max_tw_buckets (18000)
该参数设置系统的TIME_WAIT的数量,如果超过默认值则会被立即清除。
2 /proc/sys/net/ipv4/tcp_tw_reuse (0 关闭 1 打开)
该参数设置TIME_WAIT重用,可以让处于TIME_WAIT的连接用于新的tcp连接
3 /proc/sys/net/ipv4/tcp_tw_recycle ( 0 关闭 1 打开)
该参数设置tcp连接中TIME_WAIT的快速回收。
4 /proc/sys/net/ipv4/tcp_fin_timeout (60)
设置TIME_WAIT2进入CLOSED的等待时间。
网上有人提出现在大多数的解释都有错误:http://blog.renren.com/share/235269100/474012935