这里讲述三次握手,重点是讨论两个应用同时向对方发起请求的情况。

 

同时请求的情况描述:

应用程序A向B发起请求,本地端口号6001,目标端口号6002.

同时

应用程序B向A发起请求,本地端口号6002,目标端口号6001.

这时相当于互相给对方发送了SYN信号,请求建立链接。

 

这种情况在TCP/IP详解18.8,同时打开的情况可以看到。

结果是两个程序会成功建立一条从 IPaddrA:6001 到 IPaddrB:6002 的链接。

 

这里主要讨论两点:

  1. TCP如何建立链接(三次握手原理)
  2. 为什么同时请求能建立链接
  3. 能建立链接意味着什么

 

一、TCP如何建立链接(三次握手原理)

这里要讨论三次握手,本来不打算写的,翻了下博客,居然没有写过。那就写一下。

这是TCP/IP书中的示意图,讲解了基本的三次握手,四次挥手状态转换。

 

这里三次握手主要是为了什么呢?为了交换序列号Sequence Number和确认号ACK。

就是上面图中的序号,确认号。

 

交换这两个有什么用?

序列号和确认号可以提供可靠的服务。保证传送的数据,无差错,不丢失(实际上是丢失后会告知应用),不重复,且按序到达。

 

 

二、为什么同时请求能建立链接

这里我们提供一份代码,两个客户端代码大致类似,只是端口号不同。

这份代码是6001端口请求6002端口,假设它叫A,我们复制一份叫B,修改为6002端口请求6001端口。

 

然后我们用Wireshark抓包看看,他们的请求包是什么。

 

P.S. 说点题外话,在还记得2MSL等待吗?就是Time_Wait的等待。

客户端执行主动关闭后最终会进入Time_Wait状态,服务端被动关闭不进入Time_Wait状态。

这告诉我们:

  1. 终止一个客户端程序,并立即重启客户端程序,新的客户程序不能重用相同的本地端口
  2. 同时连接的情况下,主动关闭的会进入TimeWait。然后就会体会到:func connect Error Code = 10048.
  3. 所以…我现在就在等着Time_Wait的时间,毕竟我懒得重新编译了。

另外顺便讲讲为什么要有TimeWait:主动关闭方发送最后一个ACK后会进入Time_Wait状态,也就是说需要过2MSL才会回到最初的CLOSE状态。

为什么呢,主要是解决以下两点:

  1. 实现TCP全双工连接的可靠释放
  2. 使旧数据包在网络因过期而消失

 

继续,下面是我们抓包的内容:

没法做到跟书上的一致,这里它返回的太快了(RST…自己去查)。要不我试试一个程序同时两个线程搞两个请求?

代码如下:

Wireshark抓包结果如下:

Done…

对比书上的图,除了多出最终的ACK之外,完全一致。

发送SYN消息,然后双方互相发送SYN,ACK,然后最后确认一下对方发送的消息(这一步图中没有)。

通过这三次发包,完成了SEQ序列号和ACK确认号的交换。

 

三、这意味着什么

没错它完成了三次握手的本质:交换序列号和确认号。

然后接下来我要理解:

  1. 为什么会发两次SYN?
  2. 为什么两次SYN的序列号是一致的?

 

关于第一点:

最初我是简单理解为,收到SYN消息后,进入SYN_RECV状态,然后在这个状态下回复的消息都会有SYN标志。

但是实际上是客户端先发了消息,所以此时应该在SYN_SEND状态,应该接收一个SYN,ACK才对。

所以实际上应该是一个特殊的同时打开。

 

我们看下面的状态转换图:

图中有些许错误,但是不影响。

我们看,应用发送SYN包,进入SYN_SENT状态(我们认为对端和它状态一致),当它收到SYN包的情况下:

  1. 会进入同时打开状态,发送SYN, ACK数据包,
  2. 进入SYN_RECV状态,
  3. 最后收到ACK,进入ESTABLEISHED(已建立)状态。

那问题来了,它的对端也是SYN_RECV状态,那ACK由谁来发呢??

都发?没错,是该都发,接到对面的请求包,就应该回复一个确认包说自己收到了。

这是图上没有标出来的…

 

二、为什么两次SYN序列号是相同的…这个…

呵,我只能说,这说明序列号这个值的存储是跟Socket相关的,当Socket创建了之后就确定了,每次需要就读取…就讲得通了。

P.S. 一番瞎猜…没看过源码实现…丢人…

【TCP/IP】三次握手和同时请求
Tagged on:
0 0 投票数
Article Rating
订阅评论
提醒

0 评论
内联反馈
查看所有评论