传输层协议TCP (上)

news2025/2/19 14:42:52

文章目录

  • 前言
  • TCP报文格式
  • TCP连接管理
    • 连接建立与中止
    • 三次握手
      • 三次握手的状态变化
      • 为什么是三次握手
    • 四次挥手
      • 四次挥手的状态变化
      • FIN_WAIT_2 状态可能导致连接长时间不释放的问题
      • TIME_WAIT状态作用
    • 复位报文段
      • 非法连接请求
      • 其他异常情况
    • 半打开连接
    • 同时握手
    • 同时关闭
  • 参考资料


前言

  TCP(Transmission Control Protocol,传输控制协议)是互联网最重要的协议之一,为应用层提供可靠的、面向连接的数据传输服务。它广泛应用于 HTTP、FTP、SMTP 等协议中,保障数据能够准确、有序地到达目标设备。本文将主要介绍TCP连接管理机制

TCP报文格式

​ 首先我们来看TCP的报文格式
在这里插入图片描述

  • 16位源端口/目的端口 : 用于寻找发端和收端应用进程。这两个值加 上 IP 首部中的源端 IP 地址和目的端 IP 地址唯一确定一个 TCP 连接。
  • 序号 : 序号用来标识从 TCP 发端向 TCP 收端发送的数据字节流,它表示在这个报文段中的的第一 个数据字节。
  • 确认序号 : 确认这个序号以前的报文都收到了, ACK标记为 1 生效
  • 4位首部长度 : 表示该TCP头部有多少个32位bit(有多少个4字节), 首部长度范围[5,15], 所以TCP报头长度[20,60]字节
  • 6个标注位
    • URG : 紧急指针有效
    • ACK : 确认序号有效
    • PSH : 提示接收方应该尽快将这个报文交付到应用层
    • RST : 直接终止当前连接或拒绝新的连接请求, 我们把携带 RST 标识的称为复位报文段
    • SYN : 请求建立连接; 我们把携带 SYN 标识的称为同步报文段
    • FIN : 通知断开连接, 我们称携带 FIN 标识的为结束报文段
  • 16位窗口大小 : 自己的接收缓冲区的剩余空间大小
  • 16位校验和 : CRC校验, 接收端校验不通过, 则认为数据有问题
  • 16位紧急指针 : 标识哪部分数据是紧急数据

TCP连接管理

​ TCP 是一个面向连接的协议。无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接

连接建立与中止

​ 我们使用tcpdump工具来查看TCP在连接建立和中止时发生了什么, 这里使用了telnet向一个服务端发起连接

localhost.57692 > localhost.http-alt: 
Flags [S], cksum 0xfe30 (incorrect -> 0xe677), seq 2011543754, win 65495, options [mss 65495,sackOK,TS val 3010364542 ecr 0,nop,wscale 7], length 0

localhost.http-alt > localhost.57692: 
Flags [S.], cksum 0xfe30 (incorrect -> 0xaee7), seq 2821871467, ack 2011543755, win 65483, options [mss 65495,sackOK,TS val 3010364542 ecr 3010364542,nop,wscale 7], length 0

localhost.57692 > localhost.http-alt: 
Flags [.], cksum 0xfe28 (incorrect -> 0xd5a3), seq 1, ack 1, win 512, options [nop,nop,TS val 3010364542 ecr 3010364542], length 0

localhost.57692 > localhost.http-alt: 
Flags [F], cksum 0xfe28 (incorrect -> 0xc7a7), seq 1, ack 1, win 512, options [nop,nop,TS val 3010368121 ecr 3010364542], length 0

localhost.http-alt > localhost.57692: 
lags [.], cksum 0xfe28 (incorrect -> 0xb9a9), seq 1, ack 2, win 512, options [nop,nop,TS val 3010368124 ecr 3010368121], length 0

localhost.http-alt > localhost.57692: 
Flags [F], cksum 0xfe28 (incorrect -> 0xae2d), seq 1, ack 2, win 512, options [nop,nop,TS val 3010371063 ecr 3010368121], length 0

localhost.57692 > localhost.http-alt: 
Flags [.], cksum 0xa2af (correct), seq 2, ack 2, win 512, options [nop,nop,TS val 3010371063 ecr 3010371063], length 0

​ 主要观察这些标记位变化, 可以得出

  • 建立连接
    • 客户端 --> 服务端发送带有SYN标识的报文
    • 服务端 --> 客户端发送SYN+ACK
    • 客户端 --> 服务端发送ACK
  • 关闭连接
    • 客户端 --> 服务端发送带有FIN标识的报文
    • 服务端 --> 客户端发送ACK
    • 服务端 --> 客户端发送FIN
    • 客户端 --> 服务端发送ACK

有时我们也可以看到在服务端 --> 客户端 合并了FIN+ACK报文

localhost.http-alt > localhost.57692: 
Flags [F.] ......

三次握手

​ 从上面的例子来看, 为了建立一TCP条连接

  1. 请求端(通常称为客户端)发送一个 SYN 段指明打算连接的服务器的端口,
  2. 服务器发回 SYN 报文段作为应答。同时,对客户的 SYN 报文段进行确认。
  3. 客户将对服务器的 SYN 报文段进行确认。

这三个报文段完成连接的建立。这个过程也称为三次握手

三次握手的状态变化

在这里插入图片描述

  1. 一开始, 客户端和服务端都处于CLOSED状态, 然后服务端开始监听进入LISTEN状态
  2. 客户端发起请求建立连接, 发送一个带有SYN标识的报文, 客户端进入SYN_SEND状态
  3. 服务端收到客户端发送的SYN报文, 回应一个SYN+ACK, 服务端进入SYN_RCVD状态
  4. 客户端收到服务端的SYN+ACK, 回应一个ACK, 客户端三次握手完成, 进入ESTABLISHED状态
  5. 服务端收到客户端的ACK后, 服务端三次握手完成, 也进入ESTABLISHED状态

为什么是三次握手

​ TCP 是一个全双工协议,这意味着连接的双方可以同时发送和接收数据。为了确保这种能力,在连接建立时,必须确认双方都能够发送和接收数据, 三次握手可以保证双方可读可写

​ 如果是二次握手, 客户端在接收到服务端发送的ACK+SYN就已经结束了, 服务端无法得知客户端是否收到此报文, 也就是不能保证客户端是否可以接收数据, 如果服务端发送的ACK+SYN发送丢包, 此时服务端认为连接建立完成, 但实际并没有, 客户端和服务端各自等待对方的响应,最终会导致超时和连接失败

​ 三从握手是可以满足这些条件的最小次数, 三此握手可以确定双方的全双工, 如果任意时刻发生丢包, 也能通过超时重传来机制来保证

四次挥手

​ 建立一个连接需要三次握手,而终止一个连接要经过 4次握手, 从之前的例子来看

  1. 客户端发送一个FIN标识的报文表示要和服务端关闭连接
  2. 服务端应答一个ACK
  3. 服务端发送FIN表示与客户端关闭连接
  4. 客户端应答一个ACK

四次挥手的状态变化

在这里插入图片描述

  1. 客户端打算关闭连接, 发送一个FIN报文, 客户端进入FIN_WAIT_1状态
  2. 服务端应答一个ACK, 服务端进入CLOSE_WAIT状态
  3. 客户端收到服务端的ACK, 客户端进入FIN_WAIT_2状态
  4. 服务端打算关闭连接, 发送一个FIN报文, 服务端进入LAST_WAIT状态
  5. 客户端应答一个ACK, 客户端进入TIME_WAIT状态
  6. 服务端收到应答, 服务端进入CLOSED状态
  7. 客户端会在2MSL(Maximum Segment Lifetime 最大报文生命周期)后自动关闭, 进入CLOSED状态

FIN_WAIT_2 状态可能导致连接长时间不释放的问题

​ 在 FIN_WAIT_2 状态我方已经发出了 FIN ,并且另一端也已对它进行确认。除非我们在实行半关闭,否则将一直等待另一端的应用层意识到它已收到一个文件结束符说明,并向我们发一 个 FIN 来关闭另一方向的连接。只有当另一端的进程完成这个关闭,我们这端才会从 FIN_WAIT_2 状态进入 TIME_WAIT 状态。 这意味着我们这端可能永远保持这个状态。另一端也将处于 CLOSE_WAIT状态,并一直保持这个状态直到对方关闭连接

​ 某些操作系统(如 Linux)提供了超时机制,当连接在 FIN_WAIT_2状态超过一定时间后,内核会强制关闭连接,释放资源。在 Linux 中,可以通过修改 TCP 超时参数 来限制 FIN_WAIT_2 的最大存活时间

TIME_WAIT状态作用

​ TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个 MSL(maximum segment lifetime)的时间后才能回到CLOSED状态

查看MSL时间

$ cat /proc/sys/net/ipv4/tcp_fin_timeout
60

​ MSL 是任何报文段被丢弃前在网络内的最长时间, 等待2MSL时间可以保证一些陈旧报文已经消散, 同时TIME_WAIT状态依然可以接收对方的重传报文,并进行必要的应答

​ 如果没有这个状态的等待时间, 假设我们主动关闭的一方关闭后立即重启, 端口不变, 那么就有可能将一些还在网络中的陈旧报文接收到, 造成曲解, 如果客户端最后应答的ACK报文丢包, 在2MSL时间内也可以进行重传

复位报文段

​ TCP 首部中的 RST 比特是用于“复位”的。一般说来,无论何时一个报文段发往基准的连接(referenced connection)出现错误,TCP 都会发出一个复位报文段 ------《TCP/IP详解 卷1: 协议》

非法连接请求

​ 当一个数据报到达目的端口时,该端口没在使用,这是一个非法连接请求, TCP 使用复位。我们来查看Linux2.6内核源码方便理解这点

TCP对数据包的处理在tcp_v4_rcv中

/* 处理接收到的TCP数据包 */
int tcp_v4_rcv(struct sk_buff *skb)
{
	......
no_tcp_socket: /* 没有找到对应的socket,发送RST */
	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) /* 如果不符合安全策略,丢弃 */
		goto discard_it;

	if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) { /* 如果数据包长度或校验和有问题,丢弃 */
bad_packet:
		TCP_INC_STATS_BH(TCP_MIB_INERRS);
	} else {
		tcp_v4_send_reset(skb); /* 发送RST */
	}

discard_it:
	/* 丢弃数据包 */
	kfree_skb(skb);
	return 0;
	......
}

然后调用tcp_v4_send_reset发送复位报文

static void tcp_v4_send_reset(struct sk_buff *skb)
{
	struct tcphdr *th = skb->h.th;
	struct tcphdr rth;
    struct ip_reply_arg arg;

	......
        
	memset(&rth, 0, sizeof(struct tcphdr));
	rth.dest   = th->source;
	rth.source = th->dest;
	rth.doff   = sizeof(struct tcphdr) / 4;
	rth.rst    = 1;//设置RST标志

	if (th->ack) {
		rth.seq = th->ack_seq;
	} else {
		rth.ack = 1;
		rth.ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin +
					skb->len - (th->doff << 2));
	}

	memset(&arg, 0, sizeof arg);
	arg.iov[0].iov_base = (unsigned char *)&rth;
	arg.iov[0].iov_len  = sizeof rth;
	arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr,
					  skb->nh.iph->saddr, /*XXX*/
					  sizeof(struct tcphdr), IPPROTO_TCP, 0);
	arg.csumoffset = offsetof(struct tcphdr, check) / 2;

	ip_send_reply(tcp_socket->sk, skb, &arg, sizeof rth);

	TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
	TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
}

​ 我们可以看到 RST 标注位被设置

其他异常情况

  • 连接中断:当连接的另一端在未正常关闭的情况下强制终止连接(比如程序崩溃或网络故障),接收方可能会发送 RST 报文来终止连接。

  • 协议错误或违规行为:当发现协议中的违规行为(例如,接收到错误的序列号、非法的报文等),接收方可以使用 RST 报文来重置连接

半打开连接

​ 如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的 TCP 连接称为半打开连接, 半打开连接的常见原因是当客户主机突然掉电或者结束客户应用程序异常中止后再关机, 只有这条连接上没有数据传递, 服务端就不会知道客户端已经关闭, 当客户端再次打开后又会重新建立连接, 长期以往就会出现连接堆积问题, 后面我们介绍TCP的保活机制可以解决这个问题

同时握手

​ 当两端同时向对方发送 SYN 请求以建立 TCP 连接时,这种情况被称为 SYN Flood 的一种特殊形式,或者更正式地叫做 双向 SYN 同时发起。这种情形会引起连接的同时握手(Simultaneous Open)

​ TCP标准支持这种同时握手, 对于同时打开仅建立一条连接而不是两条连接, 状态转移更像双向的三次握手, 一共要交换四个报文

在这里插入图片描述

  1. 两端同时发起请求建立连接, 发送一个带有SYN标识的报文, 都进入SYN_SEND状态
  2. 两端收到对方发送发送的SYN报文, 回应一个SYN+ACK, 都进入SYN_RCVD状态
  3. 两端收到对方的SYN+ACK, 握手完成, 进入ESTABLISHED状态

同时关闭

​ 既然同时握手可能存在, 那么双方都执行主动关闭也是可能的, TCP 协议也允许这样的同时关闭(simultaneous close)

CLOSING状态就是为了处理这种特殊情况, 两端都已经发送了 FIN 包,但还未完全收到对方的确认包时

在这里插入图片描述

  1. 两端同时发送FIN报文表示关闭连接, 都进入FIN_WAIT_1状态
  2. 两端收到对方发送的FIN报文, 回应一个ACK, 两端进入CLOSING状态
  3. 两端收到对方的ACK后, 进入TIME_WAIT状态

参考资料

  • 《TCP/IP详解 卷1: 协议》
  • Linux2.6内核源码

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

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

相关文章

深度学习框架探秘|Keras:深度学习的魔法钥匙

一、引言&#xff1a;深度学习浪潮中的 Keras 前面的文章我们探秘了深度学习框架中的两大明星框架 —— TensorFlow 和 PyTorch 以及 两大框架的对比 在深度学习的众多框架中&#xff0c;还有一款框架备受开发者们的喜爱 —— Keras 。它就像是一位贴心的助手&#xff0c;为我…

2.11学习

misc buu-荷兰宽带泄露 下载附件得到了一个后缀为.bin的文件 是宽带数据文件&#xff0c;用RouterPassView工具进行查看。大多数现代路由器都可以让您备份一个文件路由器的配置文件&#xff0c;然后在需要的时候从文件中恢复配置。路由器的备份文件通常包含了像您的ISP的用户…

Python 调用 DeepSeek API 案例详细教程

本案例为以 Python 为例的调用 DeepSeek API 的小白入门级详细教程 步骤 先注册并登录 DeepSeek 官网&#xff1a;https://www.deepseek.com/ 手机号验证码注册或登录即可 创建 API KEY 注意保存&#xff0c;写代码时必须提供的 打开 Pycharm 创建工程 并安装 OpenAI 库编写代…

C++ Primer 函数基础

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

【SVN基础】

软件&#xff1a;ToritoiseSVN 代码版本回退&#xff1a;回退到上一个版本 问题&#xff1a;SVN版本已经提交了版本1和版本2&#xff0c;现在发现不需要版本2的内容&#xff0c;需要回退到版本1然后继续开发。 如图SVN版本已经提交到了107版本&#xff0c;那么本地仓库也已经…

kron积计算mask类别矩阵

文章目录 1. 生成类别矩阵如下2. pytorch 代码3. 循环移动矩阵 1. 生成类别矩阵如下 2. pytorch 代码 import torch import torch.nn as nn import torch.nn.functional as Ftorch.set_printoptions(precision3, sci_modeFalse)if __name__ "__main__":run_code 0…

Stable Diffusion 安装教程(附安装包) 【SD三种安装方式,Win+Mac一篇文章讲明白】

“Stable Diffusion的门槛过高、不会安装&#xff1f;没关系&#xff0c;这篇文章教会你如何安装&#xff01;” Stable Diffusion的安装部署其实并不困难&#xff0c;只需简单点击几下&#xff0c;几分钟就能安装好&#xff0c;不管是windows还是苹果mac电脑&#xff0c;关于…

网络安全用centos干嘛 网络安全需要学linux吗

网络安全为啥要学Linux系统&#xff0c;据不完全统计&#xff0c;Linux系统在数据中心操作系统上的份额高达70%。它一般运行于服务器和超级计算机上。 所以我们日常访问的网站后台和app后端都是部署在Linux服务器上的&#xff0c;如果你不会Linux系统操作&#xff0c;那么很多…

jupyter notebook中3种读图片的方法_与_图片翻转(上下翻转,左右翻转,上下左右翻转)

已有图片cat.jpg 相对于代码的位置&#xff0c;可以用./cat.jpg进行读取。 下面是3种读图片的方法。 1.python读图片-pillow 图片文件不适合用open去读取 用open读图片&#xff0c;易引发UnicodeDecodeError: gbk codec cant decode byte 0xff in position 0: illegal multib…

微软官方出品GPT大模型编排工具:7个开源项目

今天一起盘点下&#xff0c;12月份推荐的7个.Net开源项目&#xff08;点击标题查看详情&#xff09;。 1、一个浏览器自动化操作的.Net开源库 这是一个基于 Google 开源的 Node.js 库 Puppeteer 的 .NET 开源库&#xff0c;方便开发人员使用无头 Web 浏览器抓取 Web、检索 Ja…

机器视觉--Halcon变量的创建与赋值

一、引言 在机器视觉领域&#xff0c;Halcon 作为一款强大且功能丰富的软件库&#xff0c;为开发者提供了广泛的工具和算子来处理各种复杂的视觉任务。而变量作为程序中存储和操作数据的基本单元&#xff0c;在 Halcon 编程中起着至关重要的作用。正确地创建和赋值变量是编写高…

03【FreeRTO队列-如何获取任务信息与队列的动静态创建】

一.利用 vTaskList()以及 vTaskGetRunTimeStats()来获取任务的信息 1.现象与开启启用宏 freeRTOSConfig.h //必须启用 #define configUSE_TRACE_FACILITY 1 #define configGENERATE_RUN_TIME_STATS 1 #define configUSE_STATS_FORMATTING_FUNCTIONS…

GBD研究——美国州级地图(附资源)

美国州级别地图 地图源很多&#xff0c;随便下载。不过我试了两个资源&#xff0c;发现有的资源会漏掉阿拉斯加和夏威夷。 就剩大的这块佩奇 出现这样的问题&#xff0c;要么跟数据源有关&#xff0c;要么就是要掉地名来看&#xff0c;是不是没匹配上。 亲自试过&#xff0c…

【微服务学习一】springboot微服务项目构建以及nacos服务注册

参考链接 3. SpringCloud - 快速通关 springboot微服务项目构建 教程中使用的springboot版本是3.x&#xff0c;因此需要使用jdk17&#xff0c;并且idea也需要高版本&#xff0c;我这里使用的是IDEA2024。 环境准备好后我们就可以创建springboot项目&#xff0c;最外层的项目…

第39周:猫狗识别 2(Tensorflow实战第九周)

目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 输出 二、数据预处理 2.1 加载数据 2.2 再次检查数据 2.3 配置数据集 2.4 可视化数据 三、构建VGG-16网络 3.1 VGG-16网络介绍 3.2 搭建VGG-16模型 四、编译 五、训练模型 5.1 上次程序的主要Bug 5.2 修改版…

DeepSeek 概述与本地化部署【详细流程】

目录 一、引言 1.1 背景介绍 1.2 本地化部署的优势 二、deepseek概述 2.1 功能特点 2.2 核心优势 三、本地部署流程 3.1 版本选择 3.2 部署过程 3.2.1 下载Ollama 3.2.2 安装Ollama 3.2.3 选择 r1 模型 3.2.4 选择版本 3.2.5 本地运行deepseek模型 3.3.6 查看…

jenkins war Windows安装

Windows安装Jenkins 需求1.下载jenkins.war2.编写快速运行脚本3.启动Jenkins4.Jenkins使用 需求 1.支持在Windows下便捷运行Jenkins&#xff1b; 2.支持自定义启动参数&#xff1b; 3.有快速运行的脚步样板。 1.下载jenkins.war Jenkins下载地址&#xff1a;https://get.j…

3D打印技术:如何让古老文物重获新生?

如何让古老文物在现代社会中焕发新生是一个重要议题。传统文物保护方法虽然在一定程度上能够延缓文物的损坏&#xff0c;但在文物修复、展示和传播方面仍存在诸多局限。科技发展进步&#xff0c;3D打印技术为古老文物的保护和传承提供了全新的解决方案。我们来探讨3D打印技术如…

Vue h函数到底是个啥?

h 到底是个啥&#xff1f; 对于了解或学习Vue高阶组件&#xff08;HOC&#xff09;的同学来说&#xff0c;h() 函数无疑是一个经常遇到的概念。 那么&#xff0c;这个h() 函数究竟如何使用呢&#xff0c;又在什么场景下适合使用呢&#xff1f; 一、h 是什么 看到这个函数你可…

深入浅出 Python Logging:从基础到进阶日志管理

在 Python 开发过程中&#xff0c;日志&#xff08;Logging&#xff09;是不可或缺的调试和监控工具。合理的日志管理不仅能帮助开发者快速定位问题&#xff0c;还能提供丰富的数据支持&#xff0c;让应用更具可观测性。本文将带你全面了解 Python logging 模块&#xff0c;涵盖…