当前位置: 首页 > news >正文

Linux学习-TCP网络协议

一、TCP 基础认知

(一)协议定位与作用

TCP(Transmission Control Protocol,传输控制协议 )工作于 OSI 模型传输层,为应用层提供可靠、有序、面向连接的字节流传输服务,像 HTTP、FTP 等应用层协议依赖 TCP 保障数据稳定传输,弥补网络层(如 IP 协议)“尽力交付”的不可靠性。

(二)与 UDP 对比

特性TCPUDP
数据导向面向字节流(无明确数据包边界,按流传输 )面向数据包(每个数据包独立,有明确边界 )
连接性必须建立连接(三次握手)后通信无连接,直接发送数据包
可靠性通过确认、重传、排序等机制保障可靠传输尽力交付,不保证到达、顺序,可能丢包
资源开销机制复杂(连接管理、流量控制等 ),开销大机制简单,开销小
通信模式本质一对一(可通过多线程/进程并发实现“一对多” )支持一对一、一对多、多对多通信
典型应用网页浏览(HTTP)、文件传输(FTP)等实时音视频(直播、视频通话 )、DNS 查询等

二、TCP 核心机制全解析

(一)三次握手(建立连接,保障双向可靠)

在这里插入图片描述

1. 核心目的
  • 同步通信双方的初始序列号(ISN),为后续数据有序传输、去重做准备;
  • 确认双方发送和接收能力正常,确保连接建立后能稳定收发数据。
2. 详细流程(结合状态机理解)
  • 客户端状态变化CLOSEDSYN_SENTESTABLISHED
  • 服务端状态变化CLOSEDLISTENSYN_RCVDESTABLISHED
  • 具体交互
    1. 第一次握手(客户端→服务端)
      • 客户端发送 SYN 包(SYN = 1ACK = 0 ),携带客户端初始序列号(ISN_C),进入 SYN_SENT 状态,等待服务端回应。
      • 作用:发起连接请求,告知服务端“我要连你,这是我的初始序列号”。
    2. 第二次握手(服务端→客户端)
      • 服务端收到 SYN 后,回复 SYN + ACK 包(SYN = 1ACK = 1 ),携带服务端初始序列号(ISN_S),并确认客户端的 ISN_CACK = ISN_C + 1 ),进入 SYN_RCVD 状态。
      • 作用:同意连接请求,同步自己的序列号,同时确认客户端的序列号。
    3. 第三次握手(客户端→服务端)
      • 客户端收到 SYN + ACK 后,发送 ACK 包(ACK = 1 ),确认服务端的 ISN_SACK = ISN_S + 1 ),进入 ESTABLISHED 状态;服务端收到 ACK 后,也进入 ESTABLISHED 状态,连接正式建立。
      • 作用:最终确认服务端的序列号,双方进入“可收发数据”的稳定状态。

(二)四次挥手(断开连接,确保数据无残留)

在这里插入图片描述

1. 核心目的
  • 确保通信双方已发送的数据全部被接收,有序释放连接资源,避免数据丢失或“半关闭”状态导致的问题。
2. 详细流程(结合状态机理解)
  • 主动关闭方(假设是客户端)状态ESTABLISHEDFIN_WAIT_1FIN_WAIT_2TIME_WAITCLOSED
  • 被动关闭方(假设是服务端)状态ESTABLISHEDCLOSE_WAITLAST_ACKCLOSED
  • 具体交互
    1. 第一次挥手(主动方→被动方)
      • 主动方(如客户端)发送 FIN 包(FIN = 1 ),表示“我已无数据要发,准备断开”,进入 FIN_WAIT_1 状态。
      • 作用:发起断开请求,告知被动方“我要关闭连接了”。
    2. 第二次挥手(被动方→主动方)
      • 被动方(如服务端)收到 FIN 后,回复 ACK 包(ACK = 1ACK = 主动方FIN序列号 + 1 ),进入 CLOSE_WAIT 状态;主动方收到 ACK 后,进入 FIN_WAIT_2 状态。
      • 作用:确认断开请求,此时被动方可能仍有未发完的数据,需继续发送。
    3. 第三次挥手(被动方→主动方)
      • 被动方数据发送完毕后,发送 FIN 包(FIN = 1 ),进入 LAST_ACK 状态,告知主动方“我也没数据了,可断连”。
      • 作用:被动方完成数据发送,发起最终断开请求。
    4. 第四次挥手(主动方→被动方)
      • 主动方收到 FIN 后,回复 ACK 包(ACK = 1ACK = 被动方FIN序列号 + 1 ),进入 TIME_WAIT 状态(需等待 2MSL 时间,确保被动方收到 ACK ,避免旧包干扰新连接 );被动方收到 ACK 后,直接进入 CLOSED 状态;主动方等待 2MSL 后,也进入 CLOSED 状态,连接完全断开。
      • 作用:最终确认断开,TIME_WAIT 是 TCP 保障可靠性的关键设计(防止迟到的数据包影响后续新连接 )。

三、TCP 编程流程

在这里插入图片描述

(一)服务端流程与函数解析

1. socket():创建套接字
  • 函数原型int socket(int domain, int type, int protocol);
  • 参数
    • domain:协议族,如 AF_INET(IPv4 网络 )、AF_INET6(IPv6 )。
    • type:套接字类型,SOCK_STREAM(TCP 字节流 )、SOCK_DGRAM(UDP 数据包 )。
    • protocol:协议,一般填 0(让系统自动匹配 domaintype 对应的协议,如 AF_INET + SOCK_STREAM 对应 IPPROTO_TCP )。
  • 返回值:成功返回套接字描述符(非负整数),失败返回 -1
  • 作用:创建用于网络通信的“端点”,是后续操作的基础。
2. bind():绑定地址与端口
  • 函数原型int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数
    • sockfdsocket() 创建的套接字描述符。
    • addr:指向地址结构体的指针(如 struct sockaddr_in 用于 IPv4 ,需强转为 struct sockaddr * ),包含 IP 地址、端口等信息。
    • addrlen:地址结构体的长度(如 sizeof(struct sockaddr_in) )。
  • 返回值:成功 0,失败 -1
  • 作用:让套接字与特定 IP + 端口绑定,服务端需明确“监听哪个地址和端口的连接”。
3. listen():监听连接请求
  • 函数原型int listen(int sockfd, int backlog);
  • 参数
    • sockfd:已绑定的套接字描述符。
    • backlog半连接队列 + 全连接队列的最大长度(实际受系统限制,如 Linux 中 somaxconn 参数影响 ),表示最多允许多少个客户端处于“等待连接”状态。
  • 返回值:成功 0,失败 -1
  • 作用:将套接字设为“监听模式”,开始接收客户端的连接请求。
4. accept():接收客户端连接
  • 函数原型int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 参数
    • sockfd:监听套接字描述符(listen() 后的套接字 )。
    • addr:用于存储客户端地址信息的结构体指针(如 struct sockaddr_in ),可 NULL(不关心客户端地址 )。
    • addrlen:地址结构体长度的指针(需提前赋值,如 sizeof(struct sockaddr_in) ),函数会修改其值为实际客户端地址长度。
  • 返回值:成功返回新的通信套接字描述符(用于与该客户端收发数据 ),失败返回 -1
  • 关键逻辑:阻塞等待客户端的连接请求,三次握手完成后,生成独立的通信套接字,后续与该客户端的数据交互都通过此套接字,监听套接字可继续接收其他客户端连接。
5. recv()/send():收发数据
  • recv() 函数原型ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    • 参数
      • sockfd通信套接字描述符accept() 返回的 )。
      • buf:接收数据的缓冲区指针。
      • len:期望接收的最大字节数。
      • flags:标志位,一般填 0(默认阻塞接收 ),也可设 MSG_DONTWAIT(非阻塞 )等。
    • 返回值:成功返回实际接收的字节数;失败返回 -1;对方关闭连接返回 0
  • send() 函数原型ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    • 参数
      • sockfd:通信套接字描述符。
      • buf:要发送数据的缓冲区指针。
      • len:要发送的数据长度。
      • flags:标志位,一般填 0(默认发送 ),也可设 MSG_DONTWAIT(非阻塞 )等。
    • 返回值:成功返回实际发送的字节数;失败返回 -1
  • 作用:通过通信套接字实现应用层数据的收发,TCP 会负责底层的可靠传输(确认、重传等 )。
6. close():关闭套接字
  • 函数原型int close(int fd);fd 为套接字描述符 )
  • 作用:触发 TCP 四次挥手流程,释放套接字资源;若为监听套接字,会停止接收新连接。

(二)客户端流程与函数解析

1. socket():同服务端,创建套接字。
2. connect():发起连接请求
  • 函数原型int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数
    • sockfd:客户端套接字描述符。
    • addr:服务端地址结构体指针(如 struct sockaddr_in ,填服务端的 IP 和端口 )。
    • addrlen:地址结构体长度。
  • 返回值:成功 0,失败 -1
  • 关键逻辑:触发 TCP 三次握手,与服务端建立连接;若服务端无响应,会超时失败(可设置 SO_RCVTIMEO 等选项调整超时时间 )。
3. send()/recv():同服务端,收发数据。
4. close():同服务端,关闭套接字,发起四次挥手。

四、TCP 粘包问题)

(一)问题本质

TCP 是面向字节流的协议,无明确“数据包边界”,发送方多次发送的应用层数据,接收方可能因TCP 底层的缓存、组包机制,或接收方处理速度慢,导致“多包数据被一次性读取”,出现“粘包”(多个应用层数据包粘连在一起 ),影响解析。

(二)产生原因

  1. 发送方角度
    • 发送速率快,TCP 底层为提高效率,会将应用层的小数据包合并(Nagle 算法默认开启,合并小数据包发送 ),导致接收方读取到“合并后的大包”。
    • 应用层未明确数据包边界,连续发送时,TCP 流无区分标识。
  2. 接收方角度
    • 接收缓冲区有残留数据,recv() 调用未按“应用层数据包大小”精准读取,导致一次读出多包数据。
    • 处理数据速度慢,多个数据包在缓冲区堆积,应用层读取时“一次性取走”。

(三)解决方法

1)调整发送速率

  • 原理:降低发送方数据发送频率,减少 TCP 底层因速率快对多包数据的重新组帧,让接收方有时间逐包处理。
  • 适用场景:对实时性要求不高、数据量小且发送频率易调整的简单场景,如低并发的本地数据传输测试 。
  • 缺点:会降低整体传输效率,不适用于高并发、低延迟的业务场景,如实时音视频传输 。

2)定长数据收发(基于结构体,需注意对齐)

  • 原理:发送方按固定大小封装数据(如固定结构体长度),接收方也按相同固定大小接收,通过明确数据边界解决粘包。
  • 结构体对齐问题:跨平台(如 32 位与 64 位系统)传输时,因不同平台对结构体成员的内存布局规则不同,可能导致数据解析错误。例如:
struct a {char a; int b; long c; 
};

在 32 位和 64 位平台,int long 等类型的字节长度、内存对齐方式有差异,需用编译指令(如 #pragma pack )或属性设置(如 __attribute__((packed)) )统一对齐规则 。

  • 适用场景:数据格式固定、对跨平台兼容性要求高且数据量相对稳定的场景,如硬件设备间的简单协议通信 。
  • 示例流程:发送方定义 struct Data { char msg[100]; }; ,每次发送 100 字节的 msg 内容;接收方同样按 100 字节大小接收 struct Data 数据,解析 msg 字段 。

3)添加分隔符解析

  • 原理:在应用层发送的数据中加入唯一分隔符(如 \n ),接收方按分隔符拆分数据包,识别数据边界。
  • 示例:发送方发送 hello world\n how are you\n ,接收方读取数据后,以 \n 为标识,拆分出两个数据包 “hello world” 和 “how are you” 。
  • 注意事项:若数据本身含分隔符,需提前转义(如将数据中的 \n 替换为 \\n ,接收方再还原 ),否则会误判数据边界,导致解析错误 。
  • 适用场景:文本类数据传输,如日志传输、简单命令交互场景,分隔符易识别且数据内容对分隔符干扰少的情况 。

4)封装自定义数据帧格式(协议)

  • 原理:自定义包含帧头、帧尾、有效数据长度、校验等字段的数据帧,接收方严格按协议解析,通过帧头帧尾定位边界,校验确保数据完整。
  • 帧结构说明(以示例帧 AA C0 00 00 00 F0 00 BB 10 A0 00 00 00 10 校验 BB 为例 ):
    • 帧头:如 AA ,固定标识数据帧开始,方便接收方识别新帧 。
    • 有效数据长度:如 C0 ,告知接收方有效数据的字节数,辅助解析数据范围 。
    • 有效数据:如 00 00 00 F0 00 BB 10 A0 00 00 00 10 ,承载实际业务数据 。
    • 校验:支持 8 位和校验、16 位和校验、CRC 校验等,用于检测数据在传输中是否出错,保障数据可靠性 。
    • 帧尾:如 BB ,标识数据帧结束,配合帧头完成边界识别 。
  • 适用场景:对数据可靠性、安全性要求高的复杂场景,如工业控制、金融交易等领域的通信,需精准解析和错误校验的场景 。
  • 解析流程:接收方先查找 AA 帧头,读取有效数据长度字段,按长度提取有效数据,通过校验字段验证数据完整性,最后识别 BB 帧尾,完成一包数据解析 。
http://www.xdnf.cn/news/1356301.html

相关文章:

  • Linux shell脚本数值计算与条件执行
  • (计算机网络)JWT三部分及 Signature 作用
  • 如何在 IDEA 中在启动 Spring Boot 项目时加参数
  • [Windows] PDF-XChange Editor Plus官方便携版
  • 海盗王3.0客户端从32位升级64位之路
  • 操作系统文件系统
  • [e3nn] 等变神经网络 | 线性层o3.Linear | 非线性nn.Gate
  • Excel 转化成JSON
  • GPT 模型详解:从原理到应用
  • 第16届蓝桥杯C++中高级选拔赛(STEMA)2024年12月22日真题
  • 以国产IoTDB为代表的主流时序数据库架构与性能深度选型评测
  • 对象作为HashMap的key的注意事项
  • 30分钟通关二分查找:C语言实现+LeetCode真题
  • 机器学习算法-朴素贝叶斯
  • 优化OpenHarmony中lspci命令实现直接获取设备具体型号
  • 机械学习综合练习项目
  • 基于SpringBoot的新能源汽车租赁管理系统【2026最新】
  • Linux 系统管理核心概念与常用命令速查
  • 春秋云镜 Hospital
  • 【Qt开发】常用控件(六)
  • 一个简洁的 C++ 日志模块实现
  • 【数位DP】D. From 1 to Infinity
  • 金山办公的服务端开发工程师-25届春招笔试编程题
  • Python训练营打卡 DAY 45 Tensorboard使用介绍
  • 基于电磁频谱地图的辐射源定位算法复现
  • 基于TimeMixer现有脚本扩展的思路分析
  • 基础IO
  • CryptSIPVerifyIndirectData函数分析
  • 刷题日记0823
  • 环境 (shell) 变量