当前位置: 首页 > 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.dtcms.com/a/348418.html

相关文章:

  • 基于springboot的高校后勤保修服务系统/基于android的高校后勤保修服务系统app
  • openFeign用的什么协议,dubbo用的什么协议
  • 【重学MySQL】八十七. 触发器管理全攻略:SHOW TRIGGERS与DROP TRIGGER实战详解
  • k8s下的网络通信之calico与调度
  • MySQL官方C/C++ 接口入门
  • 从栈到堆:深入理解C语言静态与动态链表的创建与管理
  • 利旧小天才儿童电话手表实现“一键寻车”功能
  • 线程整理文档
  • 使用UE5开发《红色警戒3》类战略养成游戏的硬件配置指南
  • 【Spring Cloud 微服务】3.智能路由器——深入理解与配置负载均衡
  • MySQL的更新语句执行过程涉及了哪些文件的写入,衍生了redo、undo、二进制日志在什么时候进行写入
  • 从 JUnit 深入理解 Java 注解与反射机制
  • HarmonyOS NEXT系列之元服务框架ASCF
  • 波兰密码破译机bomba:二战密码战的隐形功臣
  • 深入OpenHarmony OTA硬核升级
  • ComfyUI ZLUDA AMD conda 使用遇到的问题
  • stm32温控大棚测控系统(CO2+温湿度+光照)+仿真
  • Docker 容器(一)
  • 【ansible】5.在受管主机部署文件和Jinja2模板
  • 信誉代币的发行和管理机制是怎样的?
  • 基于角色的访问控制(RBAC)研究与Go语言实现
  • overleaf关于给参考文献添加DOI链接的问题
  • B站视频字幕提取工具
  • 当GitHub“断网”:从应急到终极方案,手把手搭建永不宕机的代码协作体系
  • 鸿蒙 ArkTS 开发:Number、Boolean、String 三种核心基本数据类型详解(附实战案例)
  • 从 Unity UGUI 到 Unreal UMG 的无缝迁移:UMG 基础与 UI 控件布局
  • Java的数字计算
  • pycharm的matplotlib不显示动图问题的解决
  • Python核心技术开发指南(004)——配置PyCharm
  • Flink直接缓冲存储器异常解析与解决方案