TCP连接的状态图
TCP建立连接的三次握手过程,以及关闭连接的四次握手过程
贴一个telnet建立连接,断开连接的使用wireshark捕获的packet截图。
1、建立连接协议(三次握手)
(1)客户 端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1。
(2) 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通 讯。
(3) 客户必须再次回应服务段一个ACK报文,这是报文段3。
2、连接终止协议(四次握手)
由于TCP连 接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1) TCP客 户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
(3) 服务器关闭客户端的连接,发送一个FIN给客户端(报文段6)。
(4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
CLOSED: 这个没什么好说的了,表示初始状态。
LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处 于监听状态,可以接受连接了。
SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文 后,它会进入到ESTABLISHED状态。
SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
ESTABLISHED:这个容易理解了,表示连接已经建立了。
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连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
补充:
a. 默认情况下(不改变socket选项),当你调用close( or closesocket,以下说close不再重复)时,如果发送缓冲中还有数据,TCP会继续把数据发送完。
b. 发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送),但是还可以接收数据。
c. 应用层如何知道对端关闭?通常,在最简单的阻塞模型中,当你调用recv时,如果返回0,则表示对端关闭。在这个时候通常的做法就是也调用close,那么TCP层就发送FIN,继续完成四次握手。如果你不调用close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。这个可以写代码试试。
d. 在很多时候,TCP连接的断开都会由TCP层自动进行,例如你CTRL+C终止你的程序,TCP连接依然会正常关闭,你可以写代码试试。
1、 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
2、 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
什么是2MSL?MSL即Maximum Segment Lifetime,也就是报文最大生存时间,引用《TCP/IP详解》中的话:“它(MSL)是任何报文段被丢弃前在网络内的最长时间。”那么,2MSL也就是这个时间的2倍,当TCP连接完成四个报文段的交换时,主动关闭的一方将继续等待一定时间(2-4分钟),即使两端的应用程序结束。例如在上面的telnet程序客户端关闭后,使用netstat查看的结果:
C:\>netstat -na | find "172.29.21.25"
TCP 172.29.132.60:2795 172.29.21.25:23 TIME_WAIT
为什么需要这个2MSL呢,
第一,虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。
第二,报文可能会被混淆,意思是说,其他时候的连接可能会被当作本次的连接。直接引用《The TCP/IP Guide》的说法:The second is to provide a “buffering period” between the end of this connection and any subsequent ones. If not for this period, it is possible that packets from different connections could be mixed, creating confusion.
当某个连接的一端处于TIME_WAIT状态时,该连接将不能再被使用。事实上,对于我们比较有现实意义的是,这个端口将不能再被使用。某个端口处于TIME_WAIT状态(其实应该是这个连接)时,这意味着这个TCP连接并没有断开(完全断开),那么,如果你bind这个端口,就会失败。对于服务器而言,如果服务器突然crash掉了,那么它将无法在2MSL内重新启动,因为bind会失败。解决这个问题的一个方法就是设置socket的SO_REUSEADDR选项。这个选项意味着你可以重用一个地址。
当建立一个TCP连接时,服务器端会继续用原有端口监听,同时用这个端口与客户端通信。而客户端默认情况下会使用一个随机端口与服务器端的监听端口通信。有时候,为了服务器端的安全性,我们需要对客户端进行验证,即限定某个IP某个特定端口的客户端。客户端可以使用bind来使用特定的端口。对于服务器端,当设置了SO_REUSEADDR选项时,它可以在2MSL内启动并listen成功。但是对于客户端,当使用bind并设置SO_REUSEADDR时,如果在2MSL内启动,虽然bind会成功,但是在windows平台上connect会失败。而在linux上则不存在这个问题。(我的实验平台:winxp, ubuntu7.10)
要解决windows平台的这个问题,可以设置SO_LINGER选项。SO_LINGER选项决定调用close时TCP的行为。SO_LINGER涉及到linger结构体,如果设置结构体中l_onoff为非0,l_linger为0,那么调用close时TCP连接会立刻断开,TCP不会将发送缓冲中未发送的数据发送,而是立即发送一个RST报文给对方,这个时候TCP连接(关闭时)就不会进入TIME_WAIT状态。如你所见,这样做虽然解决了问题,但是并不安全。通过以上方式设置SO_LINGER状态,等同于设置SO_DONTLINGER状态。
当TCP连接发生一些物理上的意外情况时,例如网线断开,linux上的TCP实现会依然认为该连接有效,而windows则会在一定时间后返回错误信息。这似乎可以通过设置SO_KEEPALIVE选项来解决,不过不知道这个选项是否对于所有平台都有效。
3. 为什么不能用两次握手进行连接?
我们知道,3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
DoS攻击
DoS攻击、DDoS攻击和DRDoS攻击相信大家已经早有耳闻了吧!DoS是Denial of Service的简写就是拒绝服务,而DDoS就是Distributed Denial of Service的简写就是分布式拒绝服务,而DRDoS就是Distributed Reflection Denial of Service的简写,这是分布反射式拒绝服务的意思。
不过这3中攻击方法最厉害的还是DDoS,那个DRDoS攻击虽然是新近出的一种攻击方法,但它只是DDoS攻击的变形,它的唯一不同就是不用占领大量的“肉鸡”。这三种方法都是利用TCP三次握手的漏洞进行攻击的,所以对它们的防御办法都是差不多的。
DoS攻击是最早出现的,它的攻击方法说白了就是单挑,是比谁的机器性能好、速度快。但是现在的科技飞速发展,一般的网站主机都有十几台主机,而且各个主机的处理能力、内存大小和网络速度都有飞速的发展,有的网络带宽甚至超过了千兆级别。这样我们的一对一单挑式攻击就没有什么作用了,搞不好自己的机子就会死掉。举个这样的攻击例子,假如你的机器每秒能够发送10个攻击用的数据包,而被你攻击的机器(性能、网络带宽都是顶尖的)每秒能够接受并处理100攻击数据包,那样的话,你的攻击就什么用处都没有了,而且非常有死机的可能。要知道,你若是发送这种1Vs1的攻击,你的机器的CPU占用率是90%以上的,你的机器要是配置不够高的话,那你就死定了。
不过,科技在发展,黑客的技术也在发展。正所谓道高一尺,魔高一仗。经过无数次当机,黑客们终于又找到一种新的DoS攻击方法,这就是DDoS攻击。它的原理说白了就是群殴,用好多的机器对目标机器一起发动 DoS攻击,但这不是很多黑客一起参与的,这种攻击只是由一名黑客来操作的。这名黑客不是拥有很多机器,他是通过他的机器在网络上占领很多的“肉鸡”,并且控制这些“肉鸡”来发动DDoS攻击,要不然怎么叫做分布式呢。还是刚才的那个例子,你的机器每秒能发送10攻击数据包,而被攻击的机器每秒能够接受100的数据包,这样你的攻击肯定不会起作用,而你再用10台或更多的机器来对被攻击目标的机器进行攻击的话,嘿嘿!结果我就不说了。
DRDoS分布反射式拒绝服务攻击这是DDoS攻击的变形,它与DDoS的不同之处就是DrDoS不需要在攻击之前占领大量的“肉鸡”。它的攻击原理和Smurf攻击原理相近,不过DRDoS是可以在广域网上进行的,而Smurf攻击是在局域网进行的。它的作用原理是基于广播地址与回应请求的。一台计算机向另一台计算机发送一些特殊的数据包如ping请求时,会接到它的回应;如果向本网络的广播地址发送请求包,实际上会到达网络上所有的计算机,这时就会得到所有计算机的回应。这些回应是需要被接收的计算机处理的,每处理一个就要占用一份系统资源,如果同时接到网络上所有计算机的回应,接收方的系统是有可能吃不消的,就象遭到了DDoS攻击一样。不过是没有人笨到自己攻击自己,不过这种方法被黑客加以改进就具有很大的威力了。黑客向广播地址发送请求包,所有的计算机得到请求后,却不会把回应发到黑客那里,而是发到被攻击主机。这是因为黑客冒充了被攻击主机。黑客发送请求包所用的软件是可以伪造源地址的,接到伪造数据包的主机会根据源地址把回应发出去,这当然就是被攻击主机的地址。黑客同时还会把发送请求包的时间间隔减小,这样在短时间能发出大量的请求包,使被攻击主机接到从被欺骗计算机那里传来的洪水般的回应,就像遭到了DDoS攻击导致系统崩溃。骇客借助了网络中所有计算机来攻击受害者,而不需要事先去占领这些被欺骗的主机,这就是Smurf攻击。而DRDoS攻击正是这个原理,黑客同样利用特殊的发包工具,首先把伪造了源地址的SYN连接请求包发送到那些被欺骗的计算机上,根据TCP三次握手的规则,这些计算机会向源IP发出SYN+ACK或RST包来响应这个请求。同Smurf攻击一样,黑客所发送的请求包的源IP地址是被攻击主机的地址,这样受欺骗的主机就都会把回应发到被攻击主机处,造成被攻击主机忙于处理这些回应而瘫痪。
解释:
SYN:(Synchronize sequence numbers)用来建立连接,在连接请求中,SYN=1,ACK=0,连接响应时,SYN=1,ACK=1。即,SYN和ACK来区分Connection Request和Connection Accepted。
RST:(Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。如果接收到RST位时候,通常发生了某些错误。
ACK:(Acknowledgment field significant)置1时表示确认号(Acknowledgment Number)为合法,为0的时候表示数据段不包含确认信息,确认号被忽略。
设我们要准备建立连接,服务器正处于正常的接听状态。
第一步:我们也就是客户端发送一个带SYN位的请求,向服务器表示需要连接,假设请求包的序列号为10,那么则为:SYN=10,ACK=0,然后等待服务器的回应。
第二步:服务器接收到这样的请求包后,查看是否在接听的是指定的端口,如果不是就发送RST=1回应,拒绝建立连接。如果接收请求包,那么服务器发送确认回应,SYN为服务器的一个内码,假设为100,ACK位则是客户端的请求序号加1,本例中发送的数据是:SYN=100,ACK=11,用这样的数据回应给我们。向我们表示,服务器连接已经准备好了,等待我们的确认。这时我们接收到回应后,分析得到的信息,准备发送确认连接信号到服务器。
第三步:我们发送确认建立连接的信息给服务器。确认信息的SYN位是服务器发送的ACK位,ACK位是服务器发送的SYN位加1。即:SYN=11,ACK=101。
这样我们的连接就建立起来了。
DDoS究竟如何攻击?目前最流行也是最好用的攻击方法就是使用SYN-Flood进行攻击,SYN-Flood也就是SYN洪水攻击。SYN-Flood不会完成TCP三次握手的第三步,也就是不发送确认连接的信息给服务器。这样,服务器无法完成第三次握手,但服务器不会立即放弃,服务器会不停的重试并等待一定的时间后放弃这个未完成的连接,这段时间叫做SYN timeout,这段时间大约30秒-2分钟左右。若是一个用户在连接时出现问题导致服务器的一个线程等待1分钟并不是什么大不了的问题,但是若有人用特殊的软件大量模拟这种情况,那后果就可想而知了。一个服务器若是处理这些大量的半连接信息而消耗大量的系统资源和网络带宽,这样服务器就不会再有空余去处理普通用户的正常请求(因为客户的正常请求比率很小)。这样这个服务器就无法工作了,这种攻击就叫做:SYN-Flood攻击。
到目前为止,进行DDoS攻击的防御还是比较困难的。首先,这种攻击的特点是它利用了TCP/IP协议的漏洞,除非你不用TCP/IP,才有可能完全抵御住DDoS攻击。不过这不等于我们就没有办法阻挡DDoS攻击,我们可以尽力来减少DDoS的攻击。下面就是一些防御方法:
1.确保服务器的系统文件是最新的版本,并及时更新系统补丁。
2.关闭不必要的服务。
3.限制同时打开的SYN半连接数目。
4.缩短SYN半连接的time out 时间。
5.正确设置防火墙
6.禁止对主机的非开放服务的访问
7.限制特定IP地址的访问
8.启用防火墙的防DDoS的属性
9.严格限制对外开放的服务器的向外访问
10.运行端口映射程序祸端口扫描程序,要认真检查特权端口和非特权端口。
11.认真检查网络设备和主机/服务器系统的日志。只要日志出现漏洞或是时间变更,那这台机器就可能遭到了攻击。
12.限制在防火墙外与网络文件共享。这样会给黑客截取系统文件的机会,主机的信息暴露给黑客,无疑是给了对方入侵的机会。
![](https://img-my.csdn.net/uploads/201204/12/1334198744_5921.jpg)
TCP建立连接的三次握手过程,以及关闭连接的四次握手过程
![](https://img-my.csdn.net/uploads/201204/12/1334199208_5964.jpg)
贴一个telnet建立连接,断开连接的使用wireshark捕获的packet截图。
1、建立连接协议(三次握手)
(1)客户 端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1。
(2) 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通 讯。
(3) 客户必须再次回应服务段一个ACK报文,这是报文段3。
2、连接终止协议(四次握手)
由于TCP连 接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1) TCP客 户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
(3) 服务器关闭客户端的连接,发送一个FIN给客户端(报文段6)。
(4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
CLOSED: 这个没什么好说的了,表示初始状态。
LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处 于监听状态,可以接受连接了。
SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文 后,它会进入到ESTABLISHED状态。
SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
ESTABLISHED:这个容易理解了,表示连接已经建立了。
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连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
补充:
a. 默认情况下(不改变socket选项),当你调用close( or closesocket,以下说close不再重复)时,如果发送缓冲中还有数据,TCP会继续把数据发送完。
b. 发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送),但是还可以接收数据。
c. 应用层如何知道对端关闭?通常,在最简单的阻塞模型中,当你调用recv时,如果返回0,则表示对端关闭。在这个时候通常的做法就是也调用close,那么TCP层就发送FIN,继续完成四次握手。如果你不调用close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。这个可以写代码试试。
d. 在很多时候,TCP连接的断开都会由TCP层自动进行,例如你CTRL+C终止你的程序,TCP连接依然会正常关闭,你可以写代码试试。
1、 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
2、 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
什么是2MSL?MSL即Maximum Segment Lifetime,也就是报文最大生存时间,引用《TCP/IP详解》中的话:“它(MSL)是任何报文段被丢弃前在网络内的最长时间。”那么,2MSL也就是这个时间的2倍,当TCP连接完成四个报文段的交换时,主动关闭的一方将继续等待一定时间(2-4分钟),即使两端的应用程序结束。例如在上面的telnet程序客户端关闭后,使用netstat查看的结果:
C:\>netstat -na | find "172.29.21.25"
TCP 172.29.132.60:2795 172.29.21.25:23 TIME_WAIT
为什么需要这个2MSL呢,
第一,虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。
第二,报文可能会被混淆,意思是说,其他时候的连接可能会被当作本次的连接。直接引用《The TCP/IP Guide》的说法:The second is to provide a “buffering period” between the end of this connection and any subsequent ones. If not for this period, it is possible that packets from different connections could be mixed, creating confusion.
当某个连接的一端处于TIME_WAIT状态时,该连接将不能再被使用。事实上,对于我们比较有现实意义的是,这个端口将不能再被使用。某个端口处于TIME_WAIT状态(其实应该是这个连接)时,这意味着这个TCP连接并没有断开(完全断开),那么,如果你bind这个端口,就会失败。对于服务器而言,如果服务器突然crash掉了,那么它将无法在2MSL内重新启动,因为bind会失败。解决这个问题的一个方法就是设置socket的SO_REUSEADDR选项。这个选项意味着你可以重用一个地址。
当建立一个TCP连接时,服务器端会继续用原有端口监听,同时用这个端口与客户端通信。而客户端默认情况下会使用一个随机端口与服务器端的监听端口通信。有时候,为了服务器端的安全性,我们需要对客户端进行验证,即限定某个IP某个特定端口的客户端。客户端可以使用bind来使用特定的端口。对于服务器端,当设置了SO_REUSEADDR选项时,它可以在2MSL内启动并listen成功。但是对于客户端,当使用bind并设置SO_REUSEADDR时,如果在2MSL内启动,虽然bind会成功,但是在windows平台上connect会失败。而在linux上则不存在这个问题。(我的实验平台:winxp, ubuntu7.10)
要解决windows平台的这个问题,可以设置SO_LINGER选项。SO_LINGER选项决定调用close时TCP的行为。SO_LINGER涉及到linger结构体,如果设置结构体中l_onoff为非0,l_linger为0,那么调用close时TCP连接会立刻断开,TCP不会将发送缓冲中未发送的数据发送,而是立即发送一个RST报文给对方,这个时候TCP连接(关闭时)就不会进入TIME_WAIT状态。如你所见,这样做虽然解决了问题,但是并不安全。通过以上方式设置SO_LINGER状态,等同于设置SO_DONTLINGER状态。
当TCP连接发生一些物理上的意外情况时,例如网线断开,linux上的TCP实现会依然认为该连接有效,而windows则会在一定时间后返回错误信息。这似乎可以通过设置SO_KEEPALIVE选项来解决,不过不知道这个选项是否对于所有平台都有效。
3. 为什么不能用两次握手进行连接?
我们知道,3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
4、为何ISN是随机的
DoS攻击
DoS攻击、DDoS攻击和DRDoS攻击相信大家已经早有耳闻了吧!DoS是Denial of Service的简写就是拒绝服务,而DDoS就是Distributed Denial of Service的简写就是分布式拒绝服务,而DRDoS就是Distributed Reflection Denial of Service的简写,这是分布反射式拒绝服务的意思。
不过这3中攻击方法最厉害的还是DDoS,那个DRDoS攻击虽然是新近出的一种攻击方法,但它只是DDoS攻击的变形,它的唯一不同就是不用占领大量的“肉鸡”。这三种方法都是利用TCP三次握手的漏洞进行攻击的,所以对它们的防御办法都是差不多的。
DoS攻击是最早出现的,它的攻击方法说白了就是单挑,是比谁的机器性能好、速度快。但是现在的科技飞速发展,一般的网站主机都有十几台主机,而且各个主机的处理能力、内存大小和网络速度都有飞速的发展,有的网络带宽甚至超过了千兆级别。这样我们的一对一单挑式攻击就没有什么作用了,搞不好自己的机子就会死掉。举个这样的攻击例子,假如你的机器每秒能够发送10个攻击用的数据包,而被你攻击的机器(性能、网络带宽都是顶尖的)每秒能够接受并处理100攻击数据包,那样的话,你的攻击就什么用处都没有了,而且非常有死机的可能。要知道,你若是发送这种1Vs1的攻击,你的机器的CPU占用率是90%以上的,你的机器要是配置不够高的话,那你就死定了。
不过,科技在发展,黑客的技术也在发展。正所谓道高一尺,魔高一仗。经过无数次当机,黑客们终于又找到一种新的DoS攻击方法,这就是DDoS攻击。它的原理说白了就是群殴,用好多的机器对目标机器一起发动 DoS攻击,但这不是很多黑客一起参与的,这种攻击只是由一名黑客来操作的。这名黑客不是拥有很多机器,他是通过他的机器在网络上占领很多的“肉鸡”,并且控制这些“肉鸡”来发动DDoS攻击,要不然怎么叫做分布式呢。还是刚才的那个例子,你的机器每秒能发送10攻击数据包,而被攻击的机器每秒能够接受100的数据包,这样你的攻击肯定不会起作用,而你再用10台或更多的机器来对被攻击目标的机器进行攻击的话,嘿嘿!结果我就不说了。
DRDoS分布反射式拒绝服务攻击这是DDoS攻击的变形,它与DDoS的不同之处就是DrDoS不需要在攻击之前占领大量的“肉鸡”。它的攻击原理和Smurf攻击原理相近,不过DRDoS是可以在广域网上进行的,而Smurf攻击是在局域网进行的。它的作用原理是基于广播地址与回应请求的。一台计算机向另一台计算机发送一些特殊的数据包如ping请求时,会接到它的回应;如果向本网络的广播地址发送请求包,实际上会到达网络上所有的计算机,这时就会得到所有计算机的回应。这些回应是需要被接收的计算机处理的,每处理一个就要占用一份系统资源,如果同时接到网络上所有计算机的回应,接收方的系统是有可能吃不消的,就象遭到了DDoS攻击一样。不过是没有人笨到自己攻击自己,不过这种方法被黑客加以改进就具有很大的威力了。黑客向广播地址发送请求包,所有的计算机得到请求后,却不会把回应发到黑客那里,而是发到被攻击主机。这是因为黑客冒充了被攻击主机。黑客发送请求包所用的软件是可以伪造源地址的,接到伪造数据包的主机会根据源地址把回应发出去,这当然就是被攻击主机的地址。黑客同时还会把发送请求包的时间间隔减小,这样在短时间能发出大量的请求包,使被攻击主机接到从被欺骗计算机那里传来的洪水般的回应,就像遭到了DDoS攻击导致系统崩溃。骇客借助了网络中所有计算机来攻击受害者,而不需要事先去占领这些被欺骗的主机,这就是Smurf攻击。而DRDoS攻击正是这个原理,黑客同样利用特殊的发包工具,首先把伪造了源地址的SYN连接请求包发送到那些被欺骗的计算机上,根据TCP三次握手的规则,这些计算机会向源IP发出SYN+ACK或RST包来响应这个请求。同Smurf攻击一样,黑客所发送的请求包的源IP地址是被攻击主机的地址,这样受欺骗的主机就都会把回应发到被攻击主机处,造成被攻击主机忙于处理这些回应而瘫痪。
解释:
SYN:(Synchronize sequence numbers)用来建立连接,在连接请求中,SYN=1,ACK=0,连接响应时,SYN=1,ACK=1。即,SYN和ACK来区分Connection Request和Connection Accepted。
RST:(Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。如果接收到RST位时候,通常发生了某些错误。
ACK:(Acknowledgment field significant)置1时表示确认号(Acknowledgment Number)为合法,为0的时候表示数据段不包含确认信息,确认号被忽略。
设我们要准备建立连接,服务器正处于正常的接听状态。
第一步:我们也就是客户端发送一个带SYN位的请求,向服务器表示需要连接,假设请求包的序列号为10,那么则为:SYN=10,ACK=0,然后等待服务器的回应。
第二步:服务器接收到这样的请求包后,查看是否在接听的是指定的端口,如果不是就发送RST=1回应,拒绝建立连接。如果接收请求包,那么服务器发送确认回应,SYN为服务器的一个内码,假设为100,ACK位则是客户端的请求序号加1,本例中发送的数据是:SYN=100,ACK=11,用这样的数据回应给我们。向我们表示,服务器连接已经准备好了,等待我们的确认。这时我们接收到回应后,分析得到的信息,准备发送确认连接信号到服务器。
第三步:我们发送确认建立连接的信息给服务器。确认信息的SYN位是服务器发送的ACK位,ACK位是服务器发送的SYN位加1。即:SYN=11,ACK=101。
这样我们的连接就建立起来了。
DDoS究竟如何攻击?目前最流行也是最好用的攻击方法就是使用SYN-Flood进行攻击,SYN-Flood也就是SYN洪水攻击。SYN-Flood不会完成TCP三次握手的第三步,也就是不发送确认连接的信息给服务器。这样,服务器无法完成第三次握手,但服务器不会立即放弃,服务器会不停的重试并等待一定的时间后放弃这个未完成的连接,这段时间叫做SYN timeout,这段时间大约30秒-2分钟左右。若是一个用户在连接时出现问题导致服务器的一个线程等待1分钟并不是什么大不了的问题,但是若有人用特殊的软件大量模拟这种情况,那后果就可想而知了。一个服务器若是处理这些大量的半连接信息而消耗大量的系统资源和网络带宽,这样服务器就不会再有空余去处理普通用户的正常请求(因为客户的正常请求比率很小)。这样这个服务器就无法工作了,这种攻击就叫做:SYN-Flood攻击。
到目前为止,进行DDoS攻击的防御还是比较困难的。首先,这种攻击的特点是它利用了TCP/IP协议的漏洞,除非你不用TCP/IP,才有可能完全抵御住DDoS攻击。不过这不等于我们就没有办法阻挡DDoS攻击,我们可以尽力来减少DDoS的攻击。下面就是一些防御方法:
1.确保服务器的系统文件是最新的版本,并及时更新系统补丁。
2.关闭不必要的服务。
3.限制同时打开的SYN半连接数目。
4.缩短SYN半连接的time out 时间。
5.正确设置防火墙
6.禁止对主机的非开放服务的访问
7.限制特定IP地址的访问
8.启用防火墙的防DDoS的属性
9.严格限制对外开放的服务器的向外访问
10.运行端口映射程序祸端口扫描程序,要认真检查特权端口和非特权端口。
11.认真检查网络设备和主机/服务器系统的日志。只要日志出现漏洞或是时间变更,那这台机器就可能遭到了攻击。
12.限制在防火墙外与网络文件共享。这样会给黑客截取系统文件的机会,主机的信息暴露给黑客,无疑是给了对方入侵的机会。