深入理解 TCP IP 协议 -TCP 建立与终止连接

本贴最后更新于 2310 天前,其中的信息可能已经时移世易

深入理解 TCP/IP 协议-TCP 建立与终止连接

一、引言

  TCP 是一个面向连接的协议。无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。连接创建与终止的状态变化图如下:

二、三次握手建立连接

过程如下:

  • 客户端发送一个 SYN 数据包指明客户端打算连接服务器的端口,初始化序号(ISN)为 m。
  • 服务器发回包含服务器的 ISN 作为应答(值为 n)。同时,将确认序号设置成客户端 ISN+1(m+1)来作为对客户端 SYN 的确认。
  • 客户端发送一个 ACK 数据包,ack=n+1, 作为对服务器的 SYN 的确认。

1. 为什么是三次握手,而不是两次

  网络是不可靠的,数据包是可能丢失的。假设没有第三次确认,客户端向服务端发送了 SYN,请求建立连接。由于延迟,服务端没有及时收到这个包。于是客户端重新发送一个 SYN 包。回忆一下介绍 TCP 首部时提到的序列号,这两个包的序列号显然是相同的。假设服务端接收到了第二个 SYN 包,建立了通信,一段时间后通信结束,连接被关闭。这时候最初被发送的 SYN 包刚刚抵达服务端,服务端又会发送一次 ACK 确认。由于两次握手就建立了连接,此时的服务端就会建立一个新的连接,然而客户端觉得自己并没有请求建立连接,所以就不会向服务端发送数据。从而导致服务端建立了一个空的连接,白白浪费资源。

  TCP 是双通道,需要双向确定。只有两次握手,客户端知道了服务器收到了,服务器不知道客户端收到了,联想打电话。通讯系统中的占拜庭将军问题。

2. 最大报文段长度

  最大报文段长度(MSS)表示 TCP 传往另一端的最大块数据的长度。当一个连接建立时,连接的双方都要通告各自的 MSS。在三次握手的时候 SYN 的 TCP 首部中的可选字段确定。以太网的默认长度为 1460。

三、四次握手关闭连接(正常状态)

  建立一个连接需要三次握手,而终止一个连接要经过 4 次握手。这由 TCP 的半关闭 (half-close) 造成的。一个 TCP 连接是全双工(即数据在两个方向上能同时传递),因此每个方向必须单独地进行关闭。

  • 主动方想要关闭连接,发送 FIN 包给被动方,序号为 m
  • 被动方接收到主动方发送的 FIN 包,知道了对方要关闭连接,发送 ACK 确认包,序号 m+1。主动方连接关闭。
  • 等待片刻(处于半关闭状态),在此期间(finwait2,closewait)。被动方发送最后的数据,主动方接收最后的数据。
  • 被动方确认要关闭连接,发送 FIN 包。序号 n。
  • 主动方等待片刻(接收网络中,还未到达的数据包),发送 ACK 确认包。序号 n+1。到此连接关闭。

1.TCP 的半关闭状态

  TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。如主动方处于 fin_wait2 状态。

2.TIME_WAIT 状态

  TIMEWAIT 状态也称为 2MSL 等待状态。每个具体 TCP 实现必须选择一个报文段最大生存时间 MSL( Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。因为 TCP 报文段以 IP 数据报在网络内传输,而 IP 数据报则有限制其生存时间的 TTL 字段。在实际应用中,对 I P 数据报 TTL 的限制是基于跳数,而不是定时器。   在处于 2MSL 等待状态的 socket(客户端 IP 与端口,服务器 IP 与端口) 不能再被使用。但在实际的使用中,允许一个新的连接请求到达仍处于 timewait 状态的连接,只要新的序号大于该连接的前一个连接的最后序号。

四、正常状态抓包

下面是一次完整的 tcp 建立连接,发送数据,关闭连接过程

该过程为,3 次握手建立连接,一次数据发送,4 次握手关闭连接

五、异常情况

出现异常的时候,服务器通常通过复位报文来通告,复位报文为 tcp 数据包类型设置为 rst。

1. 连接超时或到达不存在的端口 / 服务器

当服务器端没有开或网络问题,会出现连接超时的情况。抓包如下:

客户端尝试 3 三次来连接,有时候服务器端会发送 rst 数据包。

2. 异常终止一个连接

  在 TCP 通讯中。如果通讯双方应为某种原因(如突然断电等)关闭连接时候一方(如 A)没有发送 fin 数据包。另一端 (如 B) 不知道对方已经关闭了连接。再次发送数据的时候,异常关闭的一方,可能会返回一个 rst 数据包。通知异常关闭。如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的 TCP 连接称为半打开 (Half Open) 的。

3. 同时打开

  两个应用程序同时彼此执行主动打开的情况是可能的。每一方必须发送一个 SYN,且这些 SYN 必须传递给对方。这需要每一方使用一个对方熟知的端口作为本地端口。同时打开的状态迁移图不同于正常状态的三次握手,该情况下需要进行 4 次握手。如图:

4. 同时关闭

  我们在以前讨论过一方(通常但不总是客户方)发送第一个 FIN 执行主动关闭。双方都执行主动关闭也是可能的,TCP 协议也允许这样的同时关闭(simultaneous close)。在同时关闭的时候,双方都进入 time_wait 状态,如图:

六. TCP 服务器设计

  大多数的 TCP 服务器进程是并发的。当一个新的连接请求到达服务器时,服务器接受这个请求,并调用一个新进程来处理这个新的客户请求。

1. 接入连接请求队列

  一个并发服务器调用一个新的进程来处理每个客户请求,因此处于被动连接请求的服务器应该始终准备处理下一个呼入的连接请求。那正是使用并发服务器的根本原因。但仍有可能出现当服务器在创建一个新的进程时,或操作系统正忙于处理优先级更高的进程时,到达多个连接请求。当服务器正处于忙时,TCP 是如何处理这些呼入的连接请求?TCP 有这样一个队列来临时存放这些连接 - 接入连接请求队列。处理方式如下:

  • 正等待连接请求的一端有一个固定长度的连接队列,该队列中的连接已被 TCP 接受(即三次握手已经完成),但还没有被应用层所接受。注意区分 TCP 接受一个连接是将其放入这个队列,而应用层接受连接是将其从该队列中移出。
  • 应用层将指明该队列的最大长度,这个值通常称为积压值 (backlog)。
  • 当一个连接请求(SYN)到达时, TCP 使用一个算法,根据当前连接队列中的连接数来确定是否接收这个连接。积压值说明的是 TCP 监听的端口已被 TCP 接受而等待应用层接受的最大连接数。
  • 如果对于新的连接请求,该 TCP 监听的端口的连接队列中还有空间,TCP 模块将对 SYN 进行确认并完成连接的建立。此时,应用层不一定知道该新的连接,如果对方发送数据,这些数据将放入缓冲队列中。
  • 如果对于新的连接请求,连接队列中已没有空间,TCP 将不理会收到的 SYN。也不发回任何报文段(即不发回 RST)。如果应用层不能及时接受已被 TCP 接受的连接,这些连接可能占满整个连接队列,客户的主动打开最终将超时。
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 165 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3194 引用 • 8214 回帖
  • tcp-ip
    1 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • AWS
    11 引用 • 28 回帖 • 11 关注
  • Postman

    Postman 是一款简单好用的 HTTP API 调试工具。

    4 引用 • 3 回帖
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    85 引用 • 165 回帖
  • SMTP

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。

    4 引用 • 18 回帖 • 637 关注
  • Access
    1 引用 • 3 回帖 • 7 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 442 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖
  • 反馈

    Communication channel for makers and users.

    126 引用 • 929 回帖 • 266 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    132 引用 • 876 回帖 • 4 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 735 关注
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    169 引用 • 1527 回帖
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    12 引用 • 54 回帖 • 20 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 299 关注
  • gRpc
    11 引用 • 9 回帖 • 89 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    71 引用 • 535 回帖 • 819 关注
  • 音乐

    你听到信仰的声音了么?

    61 引用 • 512 回帖
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 661 关注
  • 强迫症

    强迫症(OCD)属于焦虑障碍的一种类型,是一组以强迫思维和强迫行为为主要临床表现的神经精神疾病,其特点为有意识的强迫和反强迫并存,一些毫无意义、甚至违背自己意愿的想法或冲动反反复复侵入患者的日常生活。

    15 引用 • 161 回帖
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    5 引用 • 7 回帖
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 610 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    729 引用 • 1278 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    946 引用 • 1460 回帖
  • 创业

    你比 99% 的人都优秀么?

    82 引用 • 1395 回帖 • 3 关注
  • JetBrains

    JetBrains 是一家捷克的软件开发公司,该公司位于捷克的布拉格,并在俄国的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是 Java 编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA

    18 引用 • 54 回帖 • 1 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖 • 3 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖