TCP三次握手四次挥手过程分析

  情景分析:小编自己写了个服务端和客户端都是本机的TCP程序,server.c,client.c.客户端给服务器发送hello world,服务器接收到客户端传来的数据将其转换为大写,回送给客户端,客户端接收到转换后的字符串之后,主动关闭连接。都是8000这个端口

      1、使用tcpdump 抓包。

   tcpdump -iany port 8000

   2、执行服务器端程序./server

   3、执行客户端程序 ./client

 会抓到以下包,截图如下:

  

  [S] :这是一个SYN  请求包

  [S.]SYN+ACK确认包

  [.]ACK  确认包 

  以上三步表示三次握手连接过程,即client(SYN)->server(ACK+SYN)->client(ACK)

  [P.] 数据包 可以是客户端向服务器发送,也可以是服务器向客户端发送

  [F.]这是一个FIN包,服务器和客户端都可以发起

  [R] 可以理解为FIN,但是表示为连接关闭之后还有数据未发送,表示是被强制关闭的

OK下面开始讲解啦(小编刚学,这只是自己写的一份总结,不知道对不对。有错误的麻烦提出来,一起改进,么么哒)

一、TCP三次握手连接过程分析:

  (1)首先服务器端启动,开始进行监听动作,哇塞好可怕。这里采用的listen

         (2)客户端启动,开始发起连接动作,即调用,connect. 客户端发送[S],即SYN包,SYN 置位为1,ACK=0,seq=1625371597(该死的序号,会不会太大了,好像是随机生成的序号。。幸好有  CTRL+C,和CTRL+V),表明该报文段的序号是1625371597 ,然后connect陷入阻塞状态

  (3)服务器端监听到有客户端发起的连接,调用accept函数,服务器端收到客户端发送来的连接报文段,发送回一个SYN+ACK包,即SYN=1,ACK=1,确认序号ack=1625371598(刚好是客户端的seq+1,为啥咧?因为确认序号ack的意义就是期望收到的下一个包的序号,因为客户端发送的SYN包,SYN=1,TCP规定凡是SYN=1的包都消耗一个序号,所以服务器端期望接受的下一个包的序号就是seq+1啦),然后,服务器为自己选择一个序号seq=1302278192(小编很疑惑,这个序号怎么得来的?)

   OK,服务器端要发送给客户端的SYN+ACK包已经准备好啦,即SYN=1,seq=1302278192,ACK=1,ack=1625371598  然后咧,服务器的accept陷入阻塞状态,等待客户端给自己回发确认包。

  (4)客户端收到服务器发送回来的SYN+ACK包,表明服务器要和自己进行连接啦。啦啦啦 

     于是客户端给服务器最后发送一个确认报文,即ACK包,然后connect可以返回了(客户端的事情做完了)咳咳,怎么ack=1 而不是第三步的seq+1 看课本就应该是ack=seq+1=1302278192+1=1302278193啊

        小编百度了一下,似乎是TCPDUMP本身的问题,前面两次都用的绝对量,而第三次使用的是相对量,这里ack=1 表示的是相对于seq=1302278192 为1的偏移量,也就是其实ack=1302278192+1=1302278193啦,具体为何这样,小编还是菜鸟,得继续学习,有人知道的告诉我一声哈。我觉得用相对便宜挺好的,方便后面的数据传输序号,不然序号多大啊,我都凌乱了。关于这点可以戳这里 http://blog.csdn.net/qq_32503701/article/details/53559759

  注意:ACK包可以携带数据,但是如果不带数据,ACK包不消耗序列号。

    TCP规定,连接建立以后,所有传送的报文段都必须把ACK置一,因为仅当ACK=1时,报文段的确认序号才有效,而在数据传送过程中,总是需要用到ack确认序号的,索引ACK必须置一。

 

 好啦,三次握手协议就是这么多啦。 如下图

:

二、我们从图片还可以看到客户端与服务器端的数据传送流程。

   好啦,由上面可知,建立连接以后,如果是客户端主动发起传送数据请求,那么该第一个数据包的序号应该是seq=1625371598,length=12 因为我发送的是Hello World,额,我发送的是strlen("Hello world")+1 个字节哈,所以是12bytes.

  注意:这里,从图片可知,从第三步握手开始就用的都是相对偏移量作为序号了,这样看着是不是比绝对的好多啦。所以我们这里也默认:客户端的第一个数据报的seq=1,客户端的第一个数据报的序号也是seq=1(为何都是1,因为在三次握手中客户端和服务器各自都只是消耗了一个序号啊,偏移量从0开始的,因此客户端和服务器的下一个序号都是1,我猜的。哈哈)

  因此,

   (1)客户端会传送 seq=1,ack=1 和数据hello world\n 给服务器

   (2)服务器接收到数据后,发送一个确认报文段给客户端 ,即ack=1+12=13

      (3)服务器回送一个转换后的HELLO WORLD\n给客户端,seq=1,ack=13

            (4)客户端收到服务器传来的数据HELLO WORLD\n  回送一个确认报文给服务器,即ack=1+12=13 咳咳我这里居然没抓到这一步 ,

  好奇怪,有时候会有四步,即客户端发送数据,服务器确认,服务器再发送数据,客户端再确认,可是有时候最后客户端都不给服务器发送确认数据包。为何。

 

好啦,第二步的数据传送分析到这里了。

三、恩,终于到了要说再见的时候啦,客户端没有数据要发送给服务器端了,它要狠心关闭链接了。

  挥手需要四步,为何是四步呢?我们来看看好了。

  (1)客户端发送FIN报文段给服务器,说我没有数据发送给你了,我要关闭我这边的链接了。于是FIN=1 seq=13(为何是13,麻烦看上面的二。。。)

  (2)服务器收到客户端发来的结束报文,会先回送一个确认收到报文给客户端,即ack=14(为何是14,因为TCP规定,FIN不管写不写带数据都消耗一个序号),

   seq=13(这里也是看上面二) ACK=1

  (3)注意,客户端首先发起FIN,即意思是客户端没有数据传送给服务器了,并不表明客户端不能继续接收数据,如果服务器此时还有数据需要传送,则依然可以给客户端传输数据的。这就是为什么不可以在服务器接收到客户端的FIN 报文的时候服务器立刻就发送FIN报文给客户端关闭服务器到客户端的连接。而是先发送确认报文给客户端,让客户端进入FIN_WAIT2等待阶段,等待服务器把数据都传完。

  (4)服务器把数据都传完之后,会发送一个FIN报文给客户端,seq=13,FIN=1 ack=14

       (5)客户端收到服务器发送来的FIN报文之后,回送一个确认报文给服务器,即ACK=1,ack=14,seq=14

    (6)服务器接收到确认报文立刻关闭,然而客户端会进入一个2MS的等待过程,这是为什么呢?因为如果客户端收到服务器发送来的FIN 报文之后,给服务器发送确认报文过程中,报文丢失,则服务器接收不到确认报文,会超时重传,服务器会再次发起FIN报文,在这个2MS的过程中保证服务器能重发FIN报文。这样服务器能收到确认报文进入关闭状态,客户端也可以关闭啦。

 看图:嘻嘻

补充:在这里补充一下,主动发起关闭连接的一方,偶尔也会运气好的两方同时发起关闭链接啦,额关于同时发起的话,没看过源码不太知道怎么处理,其实应该无所谓,反正把其中一个当做主动方,另一个做被动方啦。(这么随意的猜测是不是太任性了,反正不管那么多啦)

  总之假设客户端主动调用close函数关闭连接,则客户端发起FIN ,客户端套接字状态切换成FIN_WAIT1 ,服务器接收到后,发送ACK给客户端,自己的套接字切换为CLOSE_WAIT状态,服务器把数据发完之后,服务器的应用程序调用close 函数给客户端发送FIN,客户端接收到后,给服务器发送ACK,客户端进行Time_wait状态,服务器接收到ACK后直接CLOSE ,时间到了,客户端也CLOSE啦。拜拜。。好像有点啰嗦。。哈哈


总结:小编这里举得例子刚刚好客户端发送的数据和服务器回传的数据长度一样,所以举例子不太好理解。人家这么认真地写总结。自己都快被感动了。。

  害羞小编也是刚刚开始看这个,这只是自己的理解,应该有很多不对的地方,所以,请指教。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值