协议簇:TCP 解析: 建立连接

简介

接前文 协议簇:TCP 解析: 基础, 我们这篇文章来看看 TCP 连接建立的过程,也就是众所周知的”三次握手“的具体流程.

系列文章

协议簇:TCP 解析:基础
协议簇:TCP 解析:建立连接
协议簇:TCP 解析:连接断开
协议簇:TCP 解析:Sequence Number
协议簇:TCP 解析:数据传输

三次握手

最普通的 TCP 握手流程如下图:
三次握手
下面描述中,序列号对应于上图中的行号.

  1. 初始状态时,TCP A 处于连接关闭状态, TCP B 处于监听状态. 也就是通常所说的 A 时 TCP 客户段,B 是服务端.
  2. A 发送 SYN 给 B, 并附有 SEQ, 请求建立 TCP 连接。
    A 发送 SYN 后,状态切换为 SYN-SENT, B 接收到 A 发送的 SYN 后状态切换为 SYN-RECEIVED.
  3. B 收到 A 的 SYN 之后,发送它的 SYN(注意,这里 A 和 B 的 SEQ 是相互独立的 ),并附上 ACK 标记用以表明 B 收到了 A 的 SYN 包。
    这里注意: B 发送的 ACK 的值为 101,它代表 B 收到了序列号为 100和100之前的所有字节数据,并告诉 A 自己期待下一次收到序列号以101开始的数据.
    A 在接收到 B 的 SYN 之后,状态转化为 ESTABLISHED.
  4. A 收到 B 的 SYN 之后, 需要发送 ACK 给 B 告诉 B 自己收到了它的 SYN + ACK 包.
    这里注意:A 发送的ACK的值为 301, 原因是 B 的 SYN 中的 SEQ 是 300. A 发送的 SEQ 是 101,原因是上一次的请求中序列号已经增长到了 100. 下一个可用的序列号就是 101.
    在B接收到 A 的 ACK 之后,它的状态切换为 ESTABLSHED. 至此,三次握手已经完成,一个 TCP 连接已经成功建立。
  5. 在这条 TCP 连接上可以进行数据传输.

在了解了基本的流程之后,我们来使用 wireshark 包应用以下所学:

如下图,忽略其中的黑色记录,一共四条记录,对应于上图中的 2-5.
在这里插入图片描述
接下来,我们详细看看每条记录

客户端 SYN

这是建立 TCP 连接三次握手中的第一次
在这里插入图片描述
这张图涵盖的信息很多,全部字段的含义在前文中已经描述过,这里我们仅仅关注个别字段.

  1. Flags 字段中 SYN 标记为 1. 表明当前 TCP 包是一个 SYN 包. 首先发送这个数据包的 TCP 段为请求建立 TCP 连接的端点.
  2. Sequence Number 字段的值为 2292773402. Wireshark 为了方便我们查看,引入另外一个字段 relative sequence number. 这个字段的值是基于 initial sequence number 计算所得. 正如前文 TCP 基础中所说,当当前 TCP 包是一个 SYN 包时,Sequence number 就是 Initial Sequence Number, 因此 这里 relative sequence number 的值是 0.
服务端 SYN + ACK

这是建立 TCP 连接三次握手中的第二次
在这里插入图片描述

  1. Flags 字段中 SYN 和 ACK 字段均为 1
  2. Sequence Number 字段的值为 4127119125 (前面说过,客户端的 SEQ 和 服务端的 SEQ 是独立的,他们之间没有联系), Relative Sequence Number 为 0.
  3. Acknowledge Number 字段的值为 2292773403. 客户段发送的 SYN 中 的 SEQ 值是 2292773402. 序列号 2292773403 告诉客户端 2292773403以及之前的所有数据已经收到。 在接收到这个响应之后, 客户端便可以确信服务段收到了自己发送的 SYN 包.
客户端 ACK

这是建立 TCP 连接三次握手中的第三次
在这里插入图片描述

  1. Flags 字段 ACK 为 1
  2. Sequence Number 的值为 2292773403. 这个值于服务端发送给我们 SYN+ACK 包中的 ACK 值相同.
  3. Acknowledge Number 的值为 4127119126. 这里需要注意服务第发送的 SYN+ACK 包中的 SEQ 的值为 4127119125.

至此,这个 TCP 连接成功建立。

特殊情况: 双方同时请求建立连接

在一个 TCP 建立成功之前,连接双方是没有任何关于对方的信息的。因此,存在一种巧合,那就是双方同时发起建立TCP连接的请求. 这里我们来看一下这个特殊情况.
在这里插入图片描述
图中 "… " 用来表示该数据包正在网络上传输,对方还未收到.

注意,这里图中虽然对数据包进行 1-7 的编号,但是对于双方任何一方,都是独立的. 也就是说,接收数据的先后是不确定的,有可能 B 先接收到 A 的包,也有可能 A 先接受到 B 的包。 这里我们并没有明确的指定哪一方为 服务端,哪一方为客户端,因此这里的结论都是成立的.

  1. 初始状态下,双方均处于 CLOSED 状态. 存在这种可能: 某一时刻双发同时发送自己的 SYN 给对方,请求建立一条 TCP 连接.
  2. 在某一时间点,A 发送 SYN 到 B 请求建立 TCP 连接. 此时,A 的状态切换为 SYN-SENT
  3. B 在未收到 A 发送的 SYN 包时,发送了自己的 SYN 给 A,请求建立一条连接. 此时,B 的状态也切换为 SYN-SENT. 在接收到 B 发送的 SYN 之后,A 的状态切换为 SYN-RECEIVED.
  4. 此时,A 发送的 SYN 到达 B 端。B 收到了 A 发送的 SYN 包,状态切换为 SYN-RECEIVED.
  5. 在第三步之后,A的状态一旦变成SYN-RECEIVED, 他就需要发送对应的 SYN+ACK 给 B,以确认自己接收到了 B 发送的 SYN. 并将自己的状态切换为 ESTABLISHED. 这里注意,它发送完 SYN+ACK 之后,只是单方的进入 ESTABLISHED 状态,对应状态依然为 SYN-RECEIVED.
  6. 在第四步之后, B的状态一旦变成SYN-RECEIVED,它也要发送 SYN+ACK. 并切换状态为 ESTABLISHED。
  7. B 收到 A 发送的 SYN+ACK。 至此,双方均进入 ESTABLISHED 状态,一个连接已然建立成功.

特殊情况: 旧的重复的 SYN

TCP 协议的设计为三次握手的一个很重要原因就是处理旧的重复的SYN包

RFC793 原文: The principle reason for the three-way handshake is to prevent old
duplicate connection initiations from causing confusion

这里我们就来看看它是如何处理的。

看下图的 TCP 包流程:
在这里插入图片描述

  1. 初始状态下, A 为客户端,处于 CLOSED 状态。 B 为服务端,处于LISTEN状态
  2. 某一时刻,A 发送 SYN 给 B,请求建立 TCP 连接
  3. B 在收到 A 刚刚发送的 SYN 包之前, 收到了一个旧的重复的来自 A 的 SYN.
  4. 对于 B 段来说,在收到来自 A 的 SYN 包时,它是不知道那是一个旧的重复的 SYN 包的,因此它就想就受到一个普通的 SYN一样响应这个 SYN 包. 发送 SYN+ACK 进行确认.
  5. A 端收到来自 B 段的 SYN+ACK, 发现其中的 ACK 字段的值不正确. 因为自己发送的 SYN 中 SEQ 值为 100,响应SYN+ACK包中的 ACK的值应该为 101,但是它收到的 SYN+ACK包中的ACK的值却是91. 在收到这个非法的SYN+ACK之后,A 段发送 RST,并附上错误的SEQ序列号. B 端收到A发送过来的 RST 之后,便得知 A 重置了上一个SYN想要建立的TCP连接,B 段重置所有关于为上一个 SYN 所记录的状态,并重新回到 LISTEN 状态.
  6. 对于 TCP 协议来说,数据是不会丢失的,也就是说 B 段迟早会收到 A 发送的 SYN(SEQ=100). B 端收到 A 的 RST 之后,状态切换到了 LISTEN之后, A 发送 SYN(SEQ=100)到达,此时B依然回像通常情况一样. 如果 SYN(SEQ=100)到达 B 段早于 A 发送的 RST,将会是一个更复杂的情况,涉及到双发均发送 RST 的场景. 这里能力有限,略掉.
  7. B 在收到正确的 SYN+ACK之后,发送自己的 SYN 给 A
  8. A 在发送自己的 SYN+ACK给B,这样之后,一个 TCP 连接成功建立.

如果剔除上述流程中 3-5,我们会发现这就是一个普通的 TCP 连接的三次握手流程. 厉害的地方在于,引入三次握手建立连接的机制,TCP 可以优雅的处理掉由于网络不可靠所导致的非法SYN的数据包。 厉害👍

Half-Open connections

这里我们将 “Half-Open connection” 称作 “半连接状态的链接”.

什么是半连接状态的链接?
B方链接已经关闭或者由于其他原因而奔溃掉,但是A方不知道B方的情况,此时 A 方持有的就是一个半连接状态的链接.

我们通过一个例子来描述半连接状态的链接
在这里插入图片描述

  1. 在某一时刻,A段的链接崩溃掉。假设奔溃的原因是A端物理内存不足. B 端未收到任何通知
  2. 在A端从错误中回复过来之后,(假设)用户尝试发送数据从原先的 TCP 连接上,A 端TCP模块会返回类似的 “connection not open”错误,此时 A 端TCP状态为 CLOSED, B 端不知道 A 端的情况,因此状态依然为 ESTABLISHED.
  3. A 端发现 TCP 错误后,尝试重新建立原先的 TCP 链接,发送SYN到 B 端
  4. B 端收到 A 的 SYN 之后,发现收到的 SEQ(400) 于自己期待的 SEQ(100) 无法匹配, 因此不对这个 SYN 进行确认,而是发送自已认为正确的 SEQ(100) 给 A 端.
  5. A 端收到 B 端发送的 SYN+ACK 之后,发现收到的 SEQ(100) 与自己刚刚发送的 SYN 中的 SEQ(400)不一致,因此得知自己的TCP状态已经与B端不一致了。因此,发送 RST 重置当前的 TCP 链接.
  6. 在上述步骤之后,上一个 TCP 链接依然被重置,双方不再维护上一个TCP链接的任何状态.
  7. 这里 A 端重新发送 SYN 请求建立一个全新的 TCP 链接. 至此,已经走上了正路,在正常的三次握手之后,一个新的 TCP 链接即将建立.
另外一种情况#####

这里还有一个更有趣的情况. 考虑下图的形况
在这里插入图片描述

  1. A 端已然崩溃掉. B 端对此一无所知.
  2. 此时, B 端认为链接一切正常,在该TCP链接上往 A 发送数据.
  3. A 端收到这个数据之后,发现这个 TCP 链接并不存在,因此直接发送 RST 通知对方重置链接.

Reset

何时 TCP 会发送 RST?
通常来说,当 TCP 接收到一个明显不是给当前连接的数据时,必须发送 RST。 当不是很明显时,不应该发送 RST.

发送 RST的原则:#####
  1. 当当前 TCP 连接不存在时,对方发送任何数据到此(除过RST),我们会发送 RST.

    在这种情况下,如果从对方收到的数据包是包含ACK,那么 RST 中的 SEQ 会被置为 ACK 中包含的 SEQ,其他情况下 RST 中的 SEQ 会被置为0且 ACK 字段被置为 SEQ + 接收到的数据的长度. 连接状态维持为 CLOSED.

  2. 当当前 TCP 连接处于正在建立的状态(LISTEN,SYN-SENT 或 SYN-RECIEVED, RFC中成为 非同步状态), 接收到的数据却包含了非法的 ACK 值,我们会发送 RST.

  3. 当当前 TCP 处于同步的状态(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), 此时收到一个非法的TCP数据包(错误的 SEQ或者错误的 ACK值), 我们应该发送一个ACK,其中包含当前的发送SEQ和一个 ACK值表明我们期待收到的下一个TCP数据包的序列号. 此时,维持当前 TCP 的状态不变.

    这种情况的一个例子就是我们在半连接状态的连接小节中第一张图的 步骤 4.

END!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499680.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

协议簇:TCP 解析: 连接断开

简介 接前文 协议簇:TCP 解析: 建立连接, 我们这篇文章来看看 TCP 连接断开的过程,也就是众所周知的”四次挥手“的具体流程. 系列文章 协议簇:TCP 解析:基础 协议簇:TCP 解析:建立连接 协议…

协议簇:TCP 解析: Sequence Number

简介 序列号(Sequence Number) 是 TCP 协议中非常重要的一个概念,以至于不得不专门来学习一下。这篇文章我们就来解开他的面纱. 在 TCP 的设计中,通过TCP协议发送的每个字节都对应于一个序列号. 由于每个字节都有自己的序列号&a…

协议簇:TCP 解析:TCP 数据传输

简介 前面,我们分别介绍了 TCP 基础知识以及连接的建立和关闭,以及最重要的 Sequence Number 的概念. 本篇文章,我们来介绍一下 TCP 如何传输数据. 系列文章 协议簇:TCP 解析:基础 协议簇:TCP 解析&…

CodeTank iOS App Technical Support

CodeTank iOS App Technical Support For All Email: z253951598outlook.com TEL: 86-17782749061 App Screen Shoots

CentOS 7 防火墙命令

查看防火墙状态 systemctl status firewalld如果已经开启,状态为 active 如果未开启,状态为 inactive 开启防火墙 systemctl start firewalld关闭防火墙 systemctl stop firewalld查看当前防火墙的配置 firewall-cmd --list-all这里,我…

Go Concurrency Patterns: Context

Go Concurrency Patterns: Context 原文地址:https://blog.golang.org/context Introduction 在 Go 语言实现的服务器上,我们总是使用 goroutine 来处理与客户端建立的连接, 给每个连接分配一个独立的 goroutine. 在请求的 handler 中也通常…

Go Concurrency Patterns: Timing out, moving on

原文地址:https://blog.golang.org/concurrency-timeouts 并发变成有它自己的风格. 一个非常好的例子就是 timeout. 虽然 go 的 channel 没有直接支持 timeout 机制,但是要实现它非常容易. 比如说,我们想从一个 channel ch 中接收数据&#…

Go Concurrency Patterns: Pipelines and cancellation

原文地址: https://blog.golang.org/pipelines 简介 Go 语言提供的并发原语使得可以很方便的构建数据流 pipeline,使用这样的 pipeline 可以高效的利用 I/O 和多 cpu 的优势. 这篇文章我们将展示如何构建并使用 pipeline. 什么是 pipeline ? 在 go 语…

QTcpSocket connectToHost 建立连接速度慢问题

问题场景 在使用 QT 开发一个客户端 App 的时候,我们通过 QTcpSocket 与后台服务器进程通信。 后台程序使用其他语言编写。 问题: 在客户端启用之后尝试建立与后台程序的 TCP 连接的时候,发现连接速度非常慢(肉眼可见的慢&#x…

WinSock I/O 模型 -- Select 模型

简介 Select 模型是 WinSock 中最常见的 I/O 模型,这篇文章我们就来看看如何使用 Select api 来实现一个简单的 TCP 服务器. API 基础 Select 模型依赖 WinSock API Select 来检查当前 Socket 是否可写或者可读。 使用这个 API 的优点是我们不需要使用阻塞的 So…

WinSock I/O 模型 -- WSAEventSelect 模型

简介 WSAEventSelect 模型也是 WinSock 中最常见的异步 I/O 模型。 这篇文章我们就来看看如何使用 WSAEventSelect api 来实现一个简单的 TCP 服务器. API 基础 WSAEventSelect WSAEventSelect 用来把一个 SOCKET 对象和一个 WSAEVENT 对象关联起来。 lNetworkEvents 表示…

WinSock I/O 模型 -- WSAAsyncSelect 模型

简介 WSAAsyncSelect 模型也是 WinSock 中常见的异步 I/O 模型。 使用这个模型,网络应用程序通过接收以 Windows 消息为基础的网络事件通知来处理网络请求。 这篇文章我们就来看看如何使用 WSAAsyncSelect api 来实现一个简单的 TCP 服务器. API 基础 要使用 W…

WinSock I/O 模型 -- OVERLAPPED I/O 模型

简介 OVERLAPPED I/O 模型也是 WinSock 中常见的异步 I/O 模型,相比于我们之前提到的 Select 模型,WSAAsyncSelect 模型 和 WSAEventSelect 模型有更好的性能. 为了方便描述,下文我们将称 Overlapped I/O 模型为 “重叠模型”. 重叠模型的…

WinSock I/O 模型 -- IOCP 模型

前言 IOCP 全称 Input/Ouput Completion Ports,中文中翻译一般为“完成端口”,本文中我们使用 IOCP 简写. IOCP 模型是迄今为止最为复杂的一种 I/O 模型,但是同时通过使用 IOCP 我们往往可以达到最佳的系统性能. 当你的网络应用程序需要管理…

GTank iOS App Technical Support

GTank iOS App Technical Support For All Email: z253951598outlook.com TEL: 86-17782749061 App Screen Shoots ​​

证书体系: CSR 解析

原文同时发布于本人个人博客: https//kutank.com/blog/cert-csr/ 简介 CSR 全称 “证书签名请求”(Certificate Signing Request). 本文我们将来详细的学习 CSR 的知识,重点集中在 CSR 所包含的信息,及其意义。 CSR 的作用: CSR 通常由想要获…

胡思乱想

学了一段时间的OGRE,才知道什么才称得上"建筑师",而我们只不过是"砌墙匠" 他们在算法算法,我们在Coding Coding,怎样才能有所改观呢~~~想当初还不如选数学专业再来学计算机可能好些, 但是既然选择了先学计算机这条路,那就先Coding,边Coding边提高数学能力…

关于数据库备份的问题

首先我们来看数据库帮助上面的sql语句: BACKUP DATABASE Northwind TO DISK c:/Northwind.bakRESTORE FILELISTONLY FROM DISK c:/Northwind.bakRESTORE DATABASE TestDB FROM DISK c:/Northwind.bak WITH MOVE Northwind TO c:/test/testdb.mdf, MOVE N…

关于函数指针调用C++非静态成员

当在类里面定义函数指针,而函数指针会指向类里面的成员的时候,这个时候成员需要定义为静态成员。实例代码如下: //.h#define SCMD_REGISTER 0class CCallFuctionList{public:CCallFuctionList();virtual ~CCallFuctionList(void);typedef…

重构心得

重构入手: 1. 找到牵连最广模块。 2. 找到上述模块中需要重构的相关的子类。 3. 原来代码不删除,保证编译运行。 4. 陆续重构其他模块 再列出我觉得可以借鉴的重构方法。【摘自代码大全】 1.保存初始代码。用你的版本控制系统保存一个初始版本&#x…