Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103410
  • 博文数量: 34
  • 博客积分: 30
  • 博客等级: 民兵
  • 技术积分: 217
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-10 23:36
文章分类
文章存档

2013年(34)

我的朋友

分类: C/C++

2013-03-28 11:13:28

在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如图1所示。
(1)第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。 【A向B请求连接】
(2)第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。 【B回应A:好的,你来吧】
(3)第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。 【A回应B:好的,我来也】
完成三次握手,客户端与服务器开始传送数据。
  .
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。【A对B说:我传完了】
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。 【B回应A:好的】
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。 【B对A说:我传完了too】
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。 【A回应B:好的,我走了】
TCP采用四次挥手关闭连接如图2所示。
  .
1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。 【收到2个“好的”才算完成。大家好才是真的好】
2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到 ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于 LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的 ACK报文
3. 控制信息字段
SYN: 同步序列编号(Synchronize Sequence Numbers)
ACK: 确认字段
FIN: 发送方已经传完数据
PSH: 推送功能
RST: 重置连接
URG: 紧急指针
4.常用状态描述


.

TCP 连接的建立和终止过程
  TCP连接是面向连接的,所谓的面向连接就是,当计算机双向通信时必需先建立连接,然后才能进行数据的传输,最后还要拆除连接。而同在一个网络层的UDP传输,是面向非连接的传输,也不是可靠的。
TCP建立连接需要三次握手的过程,而拆除连接需要四次握手的过程。
TCP连接的建立:
  1.服务器必须准备好接受外来的连接,这通过调用socket,bind,和listen函数来完成,称为被动打开。
  2.客户通过调用connect进行主动打开,这引起客户TCP发送一个SYN(Synchronize)分节(表示同步 SYN=J),它告诉服务器客户将在连接中发送的数据的初始序列号,一般情况下SYN分节不携带数据,它只包含有一
个IP头部,一个TCP头部,可能还有TCP选项。这时客户端进入SYN_SEND状态,并等待服务器确认。
3. 服务器必须确认客户的SYN,同时自己也要发送一个SYN分节,它含有服务器将在同一个连接中发送的数据的初始序列号,服务器向客户发送一个SYN(SYn=K)和对客户的ACK(J+1)。也就是SYN+ACK包。这里服务器
进入SYN_RECV状态。
4 客户必须确认服务器的SYN。这里会向服务器发送确认包ACK(ack = K+1),此包发送完毕,客户端和服务器专进入ESTABLISHED状态。至此,三次握手完成,TCP连接建立。
  完成三次握手,客户端和服务器开始传输数据。在握手过程中,ACK里的确认号为发送这个 ACK的一方所期待的对方的下一个序列号。因为SYN只占一个字节的序列号空间,所以每一个SYN的ACK中的确认号都是相应的初始序列号加1,每一个FIN的ACK的确认号为FIN的序列号加1、
  在这过程中,还有一些重要的概念需要能明白。
SYN:同步标志
同步序列编号(Synchronize Sequence Numbers)栏有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。在这里,可以把TCP序列编号看作是一个范围从0到4,294,967,295的32位计数器。通过TCP连接交换的数据中每一个字节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。所以如果你拒绝进来的syn包,它将终止其他计算机打开你计算机上的服务,但是并不会你终止你使用别的计算机的服务。

FIN:结束标志
带有该标志置位的数据包用来结束一个TCP回话,但对应端口仍处于开放状态,准备接收后续数据。
RST:复位标志
复位标志有效。用于复位相应的TCP连接。
URG:紧急标志
紧急(The urgent pointer) 标志有效。紧急标志置位,
PSH:推标志
该标志置位时,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的
 
 未连接队列:
   在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(syn=j)开设一个条目,该条目表明服务器已收到SYN 包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器 进入ESTABLISHED状态。Backlog参数:表示未连接队列的最大容纳数目。
SYN-ACK重传次数 :
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超 过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。

注意,每次重传等待的时间不一定相同。
半连接存活时间:是指半连接队列的条目存活的最长时间,也即服务从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为Timeout时间、SYN_RECV存活时间。

TCP选项:
   每一个SYN可以含有若干个TCP选项,通常使用的选项有:
MSS选项:TCP发送的SYN中,带有这个选项是通知对方它的最大分节大小MSS(maximum segment size),即它能接受的每个TCP分节中的最大数据量,可以使用TCP_MAXSEG套接口选项获取与设置这个TCP选项(Linux系统下)。
窗口规模选项:TCP双方能够通知对方的最大窗口大小是65535,因为TCP头部相应的字段只占16位,这个选项指定TCP头部的广告窗口必须扩大(左移)的位数(0--14),因此所提供的最大窗口几乎是1G字节(65535*2的14次方)
时间戳选项:这个这项对高速连接是必要的,它可以防止失而复得的分组可能造成的数据损坏,也就是说是暂时的路由原因造成的迷途的分组,当路由稳定后,它们又会正常到达目的地,其前提是它们在此前尚未被路由主动丢弃。

TCP连接终止:
TCP用三次握手建立一个连接,而终止一个连接则需要四次握手。
1. 某个应用进程首先调用close,我们称这一端执行主动关闭(active close),这一端的TCP于是发送一个FIN分节,表示数据发送完毕。
2. 接收到FIN的另一端执行被动关闭,这个FIN由TCP确认,它的接收也作为文件结束符,传递给接收方应用进程(放在已排队等候该应用进程接收的任何其他数据之后),因为FIN的接收意味着应用进程在相应连接上再也接收不到额外数据。
3. 一段时间后,接收到文件结束符的应用进程将调用close关闭它的套接口,这导致它的TCP也发送一个FIN、
4. 接收到这个FIN的源发送方TCP,对它进行确认。发送ACK、
FIN占据1个字节的序列号空间,这与SYN相同,所以每个FIN的ACK确认号是这个 FIN的序列号加1.
在步骤2与步骤3之间可以有从执行被动关闭端到执行主动关闭端的数据流,这称为半关闭。不管是客户还是服务器都可以执行主动关闭。通常情况下是客户执行主动关闭。

注意:
四次握手不是关闭TCP连接的唯一方法. 有时,如果主机需要尽快关闭连接(或连接超时,端口或主机不可达),RST (Reset)包将被发送. 注意在,由于RST包不是TCP连接中的必须部分, 可以只发送RST包(即不带ACK标记). 但在正常的TCP连接中RST包可以带ACK确认标记请注意RST包是可以不要收到方确认的
下面是一个简单的TCP连接的例子:其中涉及到了主要的数据包的变化。

握手阶段:
序号 方向    seq           ack
1  A->B 10000          0
2      B->A   20000   10000+1=10001
3     A->B   10001     20000+1=20001
解释:
1:A向B发起连接请求,以一个随机数初始化A的seq,这里假设为10000,此时ACK=0
2:B收到A的连接请求后,也以一个随机数初始化B的seq,这里假设为20000,意思是:你的请求我已收到,我这方的数据流就从这个数开始。B的ACK是A的seq加1,即10000+1=10001
3:A收到B的回复后,它的seq是它的上个请求的seq加1,即10000+1=10001,意思也是:你的回复我收到了,我这方的数据流就从这个数开始。A此时的ACK是B的seq加1,即20000+1=20001
数据传输阶段:
序号  方向    seq      ack    size
23          A->B           40000 70000 1514
24          B->A           70000 40000+1514-54=41460 54
25          A->B           41460 70000+54-54=70000 1514
26          B->A           70000 41460+1514-54=42920 54

解释:
23:B接收到A发来的seq=40000,ack=70000,size=1514的数据包
24:于是B向A也发一个数据包,告诉B,你的上个包我收到了。B的seq就以它收到的数据包的ACK填充,ACK是它收到的数据包的SEQ加上数据包的大小(不包括以太网协议头,IP头,TCP头),以证实B发过来的数据全收到了
25:A在收到B发过来的ack为41460的数据包时,一看到41460,正好是它的上个数据包的seq加上包的大小,就明白,上次发送的数据包已安全到达。于是它再发一个数据包给B。这个正在发送的数据包的seq也以它收到的数据包的ACK填充,ACK就以它收到的数据包的seq(70000)加上包的size(54)填充,即ack=70000+54-54(全是头长,没数据项)。




1. A 发起链接,SYN 置位为 1,表示请求链接,并告诉对方自己的初始序列号 200。

2. B 收到 A 的请求后,SYN 置位为 1,ACK 置位为 1,回复 A 的序列号,告诉 A 可以发送 201 了,并且告诉 A 我自己的序列号是 500。

3. A 收到 B 的回复后,ACK 置位为 1,告诉 B 可以发送 501 了,而自己本次的序列就是 201。


经过上述三次交涉,链接建立。其实这是说说的链接也是一个虚拟的概念,这里其实是有 序列号 维护的一个链接状态。

这里要说的是,为什么是三次链接,而不是两次或者四次。


首先要明确的是,TCP是一个全双工的链接。

经过 1,对 B 来说,他知道 A 是可以正常发送消息的。

经过 2,对 A 来说,他可以确认 B 是可以正常接受消息的,并且可以正常的发送消息。

经过 3,对 B 来说,他又知道 A 是可以接受消息的。 这样双方知道彼此的 收和发 都是正常的,即可以进行之后的会话。


经过以上分析可知,至少需要三次才能建立这种双方的信任,但是注意到这种信任的建立是在之前状态的基础之上,并不能保证之后的通信是正常的。

这也就引出了应该考虑第四次握手的问题,但是回发现有第四次握手就回要求第五次、第六次等等,陷入经典的 红军和蓝军 问题(可查阅相关网络教材)。

所以说三次之后的握手都是多余的。但是也说明了所有的协议都不是安全的,并不能保证本次通信信息能被对方完全接收到。


说完了三次握手建立链接,就不得不说说四次握手释放链接的问题,从下图说起


对这个途中各字段的分析在此省略,可参考上图的过程完成。

这里要说的是之所以要进行四次握手释放连接,也是由于TCP/IP是全双工的。

A 对 B 发送 FIN 报文是说我不再发送数据了。

B 对 A 发送 FIN 报文是说我也不再发送数据了。


TCP三次握手
所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。
三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息.在socket编程中,客户端执行connect()时。将触发三次握手。
  • 第一次握手:
    客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。
  • 第二次握手:
    服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1。


  • 第三次握手.
    客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1

SYN攻击

   在三次握手过程中,服务器发送SYN-ACK之后,收到客户端的ACK之前的TCP连接称为半连接(half-open connect).此时服务器处于Syn_RECV状态.当收到ACK后,服务器转入ESTABLISHED状态.

  Syn攻击就是 攻击客户端 在短时间内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直 至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。

Syn攻击是一个典型的DDOS攻击。检测SYN攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击.在Linux下可以如下命令检测是否被Syn攻击

netstat -n -p TCP | grep SYN_RECV

一般较新的TCP/IP协议栈都对这一过程进行修正来防范Syn攻击,修改tcp协议实现。主要方法有SynAttackProtect保护机制、SYN cookies技术、增加最大半连接和缩短超时时间等.

但是不能完全防范syn攻击。

TCP 四次挥手

TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。



参见wireshark抓包,实测的抓包结果并没有严格按挥手时序。我估计是时间间隔太短造成。

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可用状态了。
最后有2个问题的回答,我自己分析后的结论(不一定保证100%正确)
1、 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
2、 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
这是因为:虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。


建立TCP连接步骤(如上图3次握手):
1.服务器必须准备好接受外来的连接。这是通过调用socket、bind和listen函数来完成,称为被动打开;
2.客户通过调用connect进行主动打开。这时客户TCP会发送一个SYN(表示同步)分节,告诉服务器将在(待建立的)连接中发送数据的初始序列号。一般情况下SYN分节不携带数据,它只含有一个IP头部、一个TCP头部及可能有的TCP选项;
3.服务器必须确认客户的SYN,同时自己也得发送一个SYN分节,它含有服务器将在同一连接中发送的数据的初始序列号。服务器以单个分节向客户发送SYN和对客户SYN的ACK(表示确认);
4.客户必须确认服务器的SYN。



终止TCP连接步骤(如上图4次分手)
1.某个应用进程首先调用close,我们称这一端执行主动关闭。于是这一端的TCP会发送一个FIN分节,表示数据发送完毕;
2.接收到FIN的另一端执行被动关闭。这个FIN由TCP确认。它的接收也作为文件结束符传递给接收端应用进程(放在已排队等候应用进程接受的任务其他数据之后),因为FIN的接收意味着应用进程在相应连接上再也接收不到额外数据;
3.一段时间后(处理队列数据到FIN结束符),接收到文件结束符的应用进程将调用close关闭它的套接口。这导致它的TCP也发送一个FIN;
4.接收到这个FIN的原发送端TCP(即执行主动关闭的那一端)对它进行确认。




阅读(2063) | 评论(0) | 转发(0) |
0

上一篇:CButtonST

下一篇:tcp 长连接与短连接

给主人留下些什么吧!~~