网络通信的神奇之旅:解密Linux TCP网络协议栈的工作原理

简介: 深入探索Linux TCP网络协议栈的内部机制,揭开其背后的神秘面纱。通过对TCP协议在Linux系统中的实现方式进行详细解析,了解到它是如何实现可靠的数据传输、拥塞控制和流量管理等关键功能的。从TCP协议栈的基本构建模块开始,逐步展示数据包在协议栈中的传递过程。通过剖析各个层级的功能模块,包括物理层、链路层、网络层和传输层,我们将揭示每个模块的作用和相互配合的工作方式。同时,我们还将介绍TCP连接的建立、维护和断开过程,以及与之相关的握手机制、超时重传等关键技术。

一、TCP网络开发API

TCP,全称传输控制协议(Transmission Control Protocol),是一种面向连接的、可靠的、基于字节流的传输层通信协议。

1.1、TCP服务器调用的API

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

// 1
int socket(int domain, int type, int protocol);

// 2
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 3
int listen(int sockfd, int backlog);

// 4
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

// 5
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

// 6
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

// 7
int close(int fd);

// 8
int shutdown(int sockfd, int how);
AI 代码解读

1.2、TCP客户端调用的API

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

// 1
int socket(int domain, int type, int protocol);

// 2
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 3
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 4

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

// 5
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

// 6
int close(int fd);

// 7
int shutdown(int sockfd, int how);
AI 代码解读

1.3、API函数的作用

(1)int socket(int domain, int type, int protocol)
在文件系统中分配一个fd,并创建TCB数据结构。

(2)int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
为TCP的socket绑定本地IP地址和端口。

(3)int listen(int sockfd, int backlog)
将TCP置于LISTEN状态。

(4)int accept(int sockfd, struct sockaddr addr, socklen_t addrlen)
从全连接队列中取出一个节点,并分配一个fd。

(5)ssize_t recv(int sockfd, void *buf, size_t len, int flags)
在对应fd中,从读缓冲区中拷贝出数据。

(6)ssize_t send(int sockfd, const void *buf, size_t len, int flags)
把fd对应的TCB数据拷贝到写缓冲区中。

(7)int close(int fd)
准备一个FIN包,放到写缓冲区,是否fd。

(8)int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
准备一个SYN包,交给协议栈发送出去,等待三次握手完成后才返回。

二、TCP的三个阶段

2.1 TCP建立连接

TCP连接的建立主要依靠socket()、bind()、listen()、connect()、accept()这几个函数。

2.1.1、TCP的三次握手

示意图:
image.png

三次握手在kernel协议栈中进行,那么三次握手是在哪几个函数中发送的呢?
第一次,由connect()函数触发 发起握手,也就是发送syn包到服务端;
第二次,在listen()之后accept()之前,服务器接收到syn包后发送syn&&ack包到客户端;
第三次,客户端发送ack包到服务端完成连接的建立。

TCP报头:

  0              |1              |2              |3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-------------------------------+-------------------------------+
 |        Source Port                |        Destination Port        |
 +---------------------------------------------------------------+
|                        Sequence Number                            |
 +---------------------------------------------------------------+
|                        Acknowledgment Number                    |
 +-------+-----------+-+-+-+-+-+-+-------------------------------+
 | Header|   Reserve |U|A|P|R|S|F|            Window                |
 | Length|           |R|C|S|S|Y|I|                                |
 |       |           |G|K|H|T|N|N|                                |
 +-------------------------------+-------------------------------+
 |        Checksum                |        Urgent Pointer               |
 +---------------------------------------------------------------+
 |                            Option                                |
 +---------------------------------------------------------------+
 |                             Data                                |
  |                             ...                                |
 +---------------------------------------------------------------+
AI 代码解读
  • SYN:即synchronous,同步。
  • ACK:即acknowledgement,确认。
  • PSH:即push,推送。
  • FIN :即finish,结束。
  • RST:即reset,重置。
  • URG:即urgent,紧急。
  • Sequence Number:是数据包本身第一个字节的序列号。
  • Acknowledge Number:是期望对方继续发送的那个确认数据包的序列号其值一般为接收到的Sequence Number加1。

从报文中可以看出,SYN包最重要的是将SYN位设为1,设置Sequence Number;ACK包最重要的是将ACK位设为1,设置Acknowledgment Number。

半连接队列和全连接队列:
在三次握手中,Linux kener 协议栈会维护两个队列:半连接队列和全连接队列。
半连接队列(也叫SYN队列): 半连接队列在第一握手中,当客户端发送SYN包到服务端时,服务端的半连接队列会加入一个节点,表示此连接处于半连接状态。
全连接队列(也叫ACCEPT队列): 全连接队列在第三握手中,当客户端发送ACK包到服务端时,服务端会检查半连接队列中是否存在此连接节点(通过五元组进行查找),如果存在就将此连接节点加入全连接队列中;否则将抛弃此连接。

accpt()函数在三次握手完成后,从全连接队列中取出连接节点,为节点分配socket fd,返回到用户态。
那么,accept()函数如何知道全连接队列中有节点呢?
当三次握手完成后,全连接队列创建节点的同时会释放一个有连接接入的信号(single或信号量),这个信号决定了accept()函数是否可以从全连接队列中取节点;也决定epoll等IO多路复用器能不能检查这个连接fd是否可读。
在阻塞模式下,accept()函数一直等待信号,直到全连接队列中有节点才返回。
在非阻塞模式下,全连接队列为空accept()函数就返回-1,否则返回socket fd。

在listen()函数有,有一个backlog参数,这个参数表示的是全连接队列的大小还是半连接队列的大小呢?
随着TCP协议的不断迭代,backlog参数在不同的版本中代表的含义也不相同;它可以是半连接队列大小,也可以是全连接队列大小,也可以是半连接队列+全连接队列的大小总和。不过,效果不会有太大差异。目前版本中主要表示全连接队列的大小。

DDOS攻击:
根据三次握手原理,产生一种对服务器的攻击方式:DDOS攻击。所谓DDOS攻击,就是客户端伪造一些不存在的IP,一直发送SYN包,使服务器的半连接队列不断增大,当半连接队列的大小达到极限时,造成网络阻塞就会导致服务器无法再接受连接,从而使服务器奔溃。

2.1.2、TCP状态转换

TCP状态转换图:
image.png

(1)从状态转换图看出,LISTEN状态可以通过发送SYN和数据转换到SYN_SEND状态;也就是LISTEN状态可以发送数据。
(2)SYN_SEND状态可以收到SYN,并发送SYN和ACK转换到SYN_RECV状态;也就是两个设备可以互发SYN包,建立连接。

2.2 TCP传输数据

TCP传输数据主要依靠send()和recv()两个函数。
使用send()函数发送数据时,返回正数不一定代表发送成功。因为send()函数仅仅只是将数据拷贝到协议栈的写缓冲区,由协议栈发送;发送过程中会经过N个网关,可能存在丢包或链路断开导致未能发送到目的地。如果要知道数据是否发送成功,需要加上确认机制(ACK)。

2.2.1、传输控制块TCB

为了保证数据能正确分发,TCP使用一种TCB(传输控制块)的数据结构,把发送给不同设备的数据封装起来。这个TCB会存在整个TCP周期,知道断开连接。
一个TCB数据块包含数据发送双方对应的socket信息以及拥有存放数据的缓冲区。建立连接连接发送数据之前,通信双方必须做一个准备工作:分配内存建立TCB数据块。当双方准备好自己的socket和TCB数据结构后,就可以进入“三次握手”建立连接。

2.2.2、TCP分包

TCP分包就是要传输的数据很大,超出发送缓存区剩余空间,将会进行分包;待发送的数据大于最大报文长度,TCP在传输前将进行分包。
分包在应用程序的处理一般是发送循环send(),接收方循环recv()。

2.2.3、TCP粘包及解决方案

TCP粘包就是发送方发送的若干数据包到接收方接收时粘成一个包,从接收缓冲区看就是后数据包的头紧接着前数据包的尾。
常见解决方案:
(1)(推荐)应用层协议头前面添加包长度。分两次接收数据;第一次先接收包的长度,然后根据包的长度一次性读取或循环读取数据。
例如:

// ...
    ssize count=0;
    ssize size=0;
    while(count<tcpHdr->length)
    {
   
   
        size=recv(fd,buffer,buffersize,0);
        count+=size;
    }
// ...
AI 代码解读

(2)为每个包添加分隔符。在数据末尾添加分隔符,这会导致解数据可能需要有合包操作;因为分割数据包后,需要记录后一个数据包,用于与该包后面部分数据进行合并。

image.png

2.3 TCP四次挥手

断开连接是比建立连接和传输数据还复杂的一个过程,断开连接主要分为主动关闭和被动关闭两种。
四次挥手示意图:
image.png

需要注意的是,调用close()不是立即完成断开,而是关闭了数据传输,进入了四次挥手阶段,TCB数据结构还没有释放。四次挥手结束才真正把TCB释放。

根据四次挥手流程,可以思考一些问题:
(1)传输数据过程中,网线断了之后立刻连接,TCP如何知道?
网线掉线网卡会停止供电,再次连接后网卡恢复供电,网卡服务重启,网络连接重连。应用程序设计通过心跳包检测。
(2)服务器如何知道客户端是否宕机?
一样需要通过心跳包机制来检测。
(3)服务器如何甄别网络阻塞和宕机?
服务器发送心跳包时,不仅仅发一次,而是要发送多次的;如果是网络阻塞,那么在一定时间内一定有回复信息;如果是宕机,无论多长时间都没有客户端的回复。
(4)如果出现大量的CLOSING状态,如何处理?
出现大量CLOSING状态,基本上业务上要处理的逻辑过多,导致一直在CLOSING状态;可以使用异步,将网络层和业务层分离,单独处理。
(5)四次挥手中,为什么存在TIME_WAIT状态?
防止没有LAST_ACK或LAST_ACK丢失,导致一直重发已经不存在的socket。

总结

需要掌握TCP三次握手和四次挥手的过程,熟悉TCP状态转换。清楚什么是SYN包和ACK包。
(1)三次握手是 由客户端发起SYN,服务端收到SYN后发送SYN和ACK,客户端回复ACK;完成连接的建立。
(2)断开连接主要有主动断开和被动断开。
(3)四次挥手是 由发起方调用close(),同时发送FIN包;接收端接收到FIN包返回ACK包,接收端发送FIN包;发起方接收到FIN包返回ACK包;完成断开。
(4)理解TCP的状态转换图。LISTEN状态到SYN_RCVD状态和SYN_SEND状态,如何进入ESTABLISHED状态;四次挥手FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT、CLOSING直接的转换,CLOSE_WAIT和LAST_ACK的处理等。
(5)理解API的底层原理,以及全连接队列和半连接队列。
(6)TCP的分包场景以及TCP粘包的处理方式。

TCP通信完整过程:
image.png

image.png

相关实践学习
CentOS 8迁移Anolis OS 8
Anolis OS 8在做出差异性开发同时,在生态上和依赖管理上保持跟CentOS 8.x兼容,本文为您介绍如何通过AOMS迁移工具实现CentOS 8.x到Anolis OS 8的迁移。
相关文章
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
130 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
掌握并发模型:深度揭露网络IO复用并发模型的原理。
总结,网络 I/O 复用并发模型通过实现非阻塞 I/O、引入 I/O 复用技术如 select、poll 和 epoll,以及采用 Reactor 模式等技巧,为多任务并发提供了有效的解决方案。这样的模型有效提高了系统资源利用率,以及保证了并发任务的高效执行。在现实中,这种模型在许多网络应用程序和分布式系统中都取得了很好的应用成果。
88 35
|
4月前
|
【Linux】阻塞信号|信号原理
本教程从信号的基本概念入手,逐步讲解了阻塞信号的实现方法及其应用场景。通过对这些技术的掌握,您可以更好地控制进程在处理信号时的行为,确保应用程序在复杂的多任务环境中正常运行。
191 84
图神经网络在信息检索重排序中的应用:原理、架构与Python代码解析
本文探讨了基于图的重排序方法在信息检索领域的应用与前景。传统两阶段检索架构中,初始检索速度快但结果可能含噪声,重排序阶段通过强大语言模型提升精度,但仍面临复杂需求挑战
78 0
图神经网络在信息检索重排序中的应用:原理、架构与Python代码解析
VB6网络通信软件上位机开发,TCP网络通信,读写数据并处理,完整源码下载
本文介绍使用VB6开发网络通信上位机客户端程序,涵盖Winsock控件的引入与使用,包括连接服务端、发送数据(如通过`Winsock1.SendData`方法)及接收数据(利用`Winsock1_DataArrival`事件)。代码实现TCP网络通信,可读写并处理16进制数据,适用于自动化和工业控制领域。提供完整源码下载,适合学习VB6网络程序开发。 下载链接:[完整源码](http://xzios.cn:86/WJGL/DownLoadDetial?Id=20)
136 12
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
372 7
深入解析图神经网络注意力机制:数学原理与可视化实现
导入虚拟机到Hyper-V环境时,理解并配置网络适配器设置是确保网络通信的关键
在Hyper-V环境中,正确配置虚拟机的网络适配器是确保其网络通信的关键。需先启用Hyper-V功能并创建虚拟交换机。接着,在Hyper-V管理器中选择目标虚拟机,添加或配置网络适配器,选择合适的虚拟交换机(外部、内部或私有),并根据需求配置VLAN、MAC地址等选项。最后,启动虚拟机并验证网络连接,确保其能正常访问外部网络、与主机及其他虚拟机通信。常见问题包括无法访问外部网络或获取IP地址,需检查虚拟交换机和适配器设置。
应用程序中的网络协议:原理、应用与挑战
网络协议是应用程序实现流畅运行和安全通信的基石。了解不同协议的特点和应用场景,以及它们面临的挑战和应对策略,对于开发者和用户都具有重要意义。在未来,随着技术的不断发展,网络协议也将不断优化和创新,为数字世界的发展提供更强大的支持。
147 1
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
软演员-评论家算法(Soft Actor-Critic, SAC)是深度强化学习领域的重要进展,基于最大熵框架优化策略,在探索与利用之间实现动态平衡。SAC通过双Q网络设计和自适应温度参数,提升了训练稳定性和样本效率。本文详细解析了SAC的数学原理、网络架构及PyTorch实现,涵盖演员网络的动作采样与对数概率计算、评论家网络的Q值估计及其损失函数,并介绍了完整的SAC智能体实现流程。SAC在连续动作空间中表现出色,具有高样本效率和稳定的训练过程,适合实际应用场景。
1406 7
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
【网络原理】——HTTP协议、fiddler抓包
HTTP超文本传输,HTML,fiddler抓包,URL,urlencode,HTTP首行方法,GET方法,POST方法
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问