【Linux网络】基于TCP的全连接队列与文件、套接字、内核之间的关系
W...Y的主页 😊
代码仓库管理💕
前言:之前我们已经学习了TCP传输协议,而无论是TCP还是UDP都是使用socket套接字进行网络传输的,而TCP的socket是比UDP复杂的,当时我们学习TCPsocket编程时使用listen函数进行监听,今天我们就来说说listen函数第二个参数的含义,并且打通socket与内核之间的关系!!
目录
理解 listen 的第二个参数
理解全连接队列
在内核中理解全连接队列与套接字
理解 listen 的第二个参数
基于刚才封装的 TcpSocket 实现以下测试代码。对于服务器, listen 的第二个参数设置为 1, 并且不调用 accept。不调用accept就无法建立连接,导致这些连接全部会在全连接队列中(全连接队列是什么我们会在后面讲述)。
test_server.cc
#include "tcp_socket.hpp"
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage ./test_server [ip] [port]\n");
return 1;
}
TcpSocket sock;
bool ret = sock.Bind(argv[1], atoi(argv[2]));
if (!ret) {
return 1;
}
ret = sock.Listen(2);
if (!ret) {
return 1;
}
// 客户端不进行 accept
while (1) {
sleep(1);
}
return 0;
}
test_client.cc
#include "tcp_socket.hpp"
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage ./test_client [ip] [port]\n");
return 1;
}
TcpSocket sock;
bool ret = sock.Connect(argv[1], atoi(argv[2]));
if (ret) {
printf("connect ok\n");
} else {
printf("connect failed\n");
}
while (1) {
sleep(1);
}
return 0;
}
此时启动 3 个客户端同时连接服务器, 用 netstat 查看服务器状态, 一切正常,但是启动第四个客户端时, 发现服务器对于第四个连接的状态存在问题了。
tcp 3 0 0.0.0.0:9090 0.0.0.0:*
LISTEN 9084/./test_server
tcp 0 0 127.0.0.1:9090 127.0.0.1:48178
SYN_RECV -
tcp 0 0 127.0.0.1:9090 127.0.0.1:48176
ESTABLISHED -
tcp 0 0 127.0.0.1:48178 127.0.0.1:9090
ESTABLISHED 9140/./test_client
tcp 0 0 127.0.0.1:48174 127.0.0.1:9090
ESTABLISHED 9087/./test_client
tcp 0 0 127.0.0.1:48176 127.0.0.1:9090
ESTABLISHED 9088/./test_client
tcp 0 0 127.0.0.1:48172 127.0.0.1:9090
ESTABLISHED 9086/./test_client
tcp 0 0 127.0.0.1:9090 127.0.0.1:48174
ESTABLISHED -
tcp 0 0 127.0.0.1:9090 127.0.0.1:48172
ESTABLISHED -
前三个连接状态都是established,而第四个连接的客户端的状态是syn_sent(链接同步发送状态)是没有连接成功的,三次握手都没有成功。往后所以的客户端想要连接都会是syn_sent状态。
所以我们得出结论:在服务器来不及accept时,底层的TCP listen socket允许用户进行三次握手建立成功连接,但是不能建立太多,最多能建立backlog + 1个连接。 而这个backlog就是listen函数的第二个参数。
我们将这些建立好连接的链接会放在一个队列来维护,这个队列就是全连接队列。
客户端状态正常, 但是服务器端出现了 SYN_RECV 状态, 而不是 ESTABLISHED 状态这是因为, Linux 内核协议栈为一个 tcp 连接管理使用两个队列:
1. 半链接队列(用来保存处于 SYN_SENT 和 SYN_RECV 状态的请求)
2. 全连接队列(accpetd 队列)(用来保存处于 established 状态,但是应用层没有调用 accept 取走的请求)
而全连接队列的长度会受到 listen 第二个参数的影响.
全连接队列满了的时候, 就无法继续让当前连接的状态进入 established 状态了.
这个队列的长度通过上述实验可知, 是 listen 的第二个参数 + 1.
理解全连接队列
连接本质是内核中的一种数据结构,而我们使用socket套接字中的accept就是为了将已经建立好的连接拿上来进行使用。accpet函数在建立连接的同时,还能获取客户端的地址和端口信息。
我们不能将backlog中参数和服务器只能处理backlog+1个连接所混淆,服务器可以处理多个请求,这些连接就会被拿到上层不再全连接队列中,而全连接队列中的连接是已经建立好三次握手连接的却来不及接收的连接!!!
而我们不能不要这个全连接队列,因为这个全连接队列就相当于一个缓冲区,这个模式就是生产消费者模型,如果服务端突然空闲那就可以连接全连接队列中的连接,提高了整个服务端的吞吐量和用户的体验。如果没有全连接队列就会增加服务端的闲置率!
我们也不能设置太长,如果设置太长那么晚来的客户端就在队列的尾部也会等很长时间,许多用户等不住就会关掉连接,但是还没有达到最终目的浪费了用户时间并且浪费服务器维护队列空间。
在内核中理解全连接队列与套接字
在Linux内核中,task_struct结构体是描述进程的核心数据结构,它包含了控制和管理进程所必需的全部信息。 而struct file_struct结构体是用啦维护文件描述符的一个数据结构,他记录了一个进程打开的所有文件消息,file_struct中有一个fd数组用来记录文件描述符的。
在Linux内核中,struct file
结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct file
实例。
所以使用listen_socket打开一个文件,就有一个文件描述符指向打开的struck file中,这时我们所理解的。
而我们使用socket创建套接字时,内核就为我们创建了一个struct socket结构体,struct file 与struct socket用*private_data指针关联,上图就是我们结构体的内容。struct socket结构体就是我们网络的入口。
但真实的情况是创建一个TCP连接,而真正的应该是一个tcp_sock的结构体。而tcp是面向连接的,里面有一个字段也是一个结构体是struct inet_connection_sock,他说tcp_sock中第一个字段,这个结构体包含了tcp链接的相关消息(超时时间……),里面有个字段是struct request_sock_queue icsk_accept_queue,这个就是全连接队列。而struct inet_connection_sock中第一个字段又是一个结构体struct inet_sock(里面有源端口、目的端口、源IP、目的IP……)而struct inet_sock里面第一个字段又是一个结构体struct sock
而我们的struct socket结构体中有一个字段struct sock* sk指针,这个指针就可以指向struct tcp_sock结构体并且使用强转就可以对tcp_sock里面的所有字段进行访问。这就是C风格的多态。
而我们的UDP与TCP大相径庭,只不过UDP是无连接的,所以在udp_sock中没有struct inet_connection_sock字段,剩下都是一样的。
我们的struct socket中有一个字段struct proto_ops结构体指针,里面存放了UDP与TCP的函数方法集。所以我们的struct socket是基类,tcp_sock与udp_sock都是子类。我们把struct socket叫做BSD socket——通用socket接口,把子类叫做inet_sock
上述是我们讲述的创建一个listen套接字,而如何获取一个连接呢(accept)?
当我们进行三次握手成功后就会建立tcp_sock结构体,这个结构体会被连入全连接队列中,后面我们进行accept时就会创建struct file 然后再创建struct socket,之后将struct file 与struct socket用*private_data指针关联起来并在文件描述符表中申请一个fd(这些操作都是由一个函数(映射)完成的(sock_map_fd)),然后就会再全连接队列中获取tcp_sock结构体进行关联,直接用struct socket中的sock指针指向tcp_sock结构体,这样我们的一个普通套接字也有上述对应的结构了。
这就是在内核中socket、UDP、TCP、全连接队列、文件所对应的关系,下面我们用一张非常简单的图来表示对应关系:
详细可以参考源码,此处不展示!!!
相关文章:

【Linux网络】基于TCP的全连接队列与文件、套接字、内核之间的关系
W...Y的主页 😊 代码仓库管理💕 前言:之前我们已经学习了TCP传输协议,而无论是TCP还是UDP都是使用socket套接字进行网络传输的,而TCP的socket是比UDP复杂的,当时我们学习TCPsocket编程时使用listen函数进行…...
IDE(集成开发环境)
IDE(集成开发环境)是软件开发过程中不可或缺的工具,它集成了代码编写功能、分析功能、编译器、调试器等开发工具,旨在提高开发效率。不同的IDE支持不同的语言和框架,下面是一些通用的IDE使用技巧和插件推荐,…...

一键导入Excel到阿里云PolarDB-MySQL版
今天,我将分享如何一键导入Excel到阿里云PolarDB-MySQL版数据库。 准备数据 这里,我们准备了一张excel表格如下: 连接到阿里云PolarDB 打开的卢导表,点击新建连接-选择阿里云PolarDB-MySQL版。如果你还没有这个工具,…...
Oracle有哪些版本
目录 Oracle 1(1979年) Oracle 2(1983年) Oracle 7(1992年) Oracle 8i(1999年) Oracle 9i(2001年) Oracle 10g(2004年) Oracle 11g(2007年) Oracle 12c(2013年) Oracle 18c(2018年) Oracle 19c(2019年) Oracle 21c(2023年) Oracle 23ai(202…...
先来先服务(FCFS,First-Come, First-Served)调度算法
有利于CPU繁忙作业的原因 充分利用CPU资源: 当一个CPU繁忙型的作业到达后,它会立即被执行,并且在没有其他作业等待的情况下,可以一直占用CPU直到完成。这使得CPU能够持续地执行作业,最大化利用CPU资源。 减少上下文切换…...
Windows操作系统忘记密码怎么办 这个方法屡试不爽 还不来试一下
Windows操作系统重置密码的操作步骤如下: 本方法适用于Windows Server 2008R2及其之后的操作系统。 第一步:从Windows 2008R2之后的操作系统光盘启动到安装界面,一直下一步到磁盘分区界面,按shiftF10调出cmd命令行界面。 第二步&…...

基于java的山区环境监督管理系统(源码+定制+开发)环境数据可视化、环境数据监测、 环境保护管理 、污染防治监测系统 大数据分析
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
jQuery Mobile 表单输入
jQuery Mobile 表单输入 引言 在移动设备上,表单输入是用户与移动应用交互的重要方式。jQuery Mobile 是一个基于 jQuery 的移动设备友好的开发框架,它提供了丰富的组件和工具来帮助开发者创建响应式和交互式的移动界面。本文将详细介绍如何使用 jQuery Mobile 来创建和定制…...

IoC详解
共有两类注解类型可以实现: 1. 类注解:Controller、Service、Repository、Component、Configuration. 2. 方法注解:Bean. 类注解 Controller(控制器存储) 使⽤Controller存储bean的代码如下所⽰: Con…...

基于 ThinkPHP+Mysql 灵活用工_灵活用工系统_灵活用工平台
基于 ThinkPHPMysql 灵活用工灵活用工平台灵活用工系统灵活用工小程序灵活用工源码灵活用工系统源码 开发语言 ThinkPHPMysql 源码合作 提供完整源代码 软件界面展示 一、企业管理后台 二、运用管理平台 三、手机端...
etcd之etcd分布式锁及事务(四)
1、etcd分布式锁及事务 1.1 前言 分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如 果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要…...

智慧旅游微信小程序平台
作者介绍:✌️大厂全栈码农|毕设实战开发,专注于大学生项目实战开发、讲解和毕业答疑辅导。 🍅获取源码联系方式请查看文末🍅 推荐订阅精彩专栏 👇🏻 避免错过下次更新 Springboot项目精选实战案例 更多项目…...

C++设计模式创建型模式———简单工厂模式、工厂方法模式、抽象工厂模式
文章目录 一、引言二、简单工厂模式三、工厂方法模式三、抽象工厂模式四、总结 一、引言 创建一个类对象的传统方式是使用关键字new , 因为用 new 创建的类对象是一个堆对象,可以实现多态。工厂模式通过把创建对象的代码包装起来,实现创建对…...

C++ 类与对象(中) 默认成员函数
我们知道在类中,有成员变量和成员函数,我们可以通过创造不同的成员函数来实现这个类不同的功能,如果我们创造一个类,却不实现它的成员函数会如何呢?这个就涉及到类中的默认成员函数的概念了。但在本文我们主要介绍以下…...
中间人攻击(https降级攻击)和iptables命令分析
中间人攻击 以下是一个简单的中间人攻击示例,结合 ARP 欺骗和流量修改: 1. 进行 ARP 欺骗 首先,使用 arpspoof 进行 ARP 欺骗,将受害者的流量重定向到攻击者的机器上: sudo arpspoof -i eth0 -t 172.29.144.50 172…...
开源生活-分布式管理
开源竞争(当自己没有办法彻底掌握一门技术的时候就彻底开源掉;培养出更多的依赖,让更多人帮助你完善你的技术,那么这不就是在砸罐子吗?一个行业里面总会有人先砸罐子的,你不如先砸罐子,还能听个…...
华为OD机试真题- 关联子串
该专栏题目包含两部分: 100 分值部分题目 200 分值部分题目 所有题目都会陆续更新,订阅防丢失 题目描述: 给定两个字符串str1和str2,如果字符串str1中的字符,经过排列组合后的字符串中,只要有一个字符串是…...

云智慧完成华为原生鸿蒙系统的适配, 透视宝 APM 为用户体验保驾护航
2024 年 10 月 22 日,首个国产移动操作系统 —— 华为原生鸿蒙操作系统 HarmonyOS NEXT 正式面世,成为继 iOS 和 Android 后的全球第三大移动操作系统。HarmonyOS NEXT,从系统内核、数据库根基,到编程语言创新、AI(人工…...

QT 多语言转换 ts、qm
QT开发之路 企业级开发系列文章,主要目标快速学习、完善、提升 相关技能 高效完成企业级项目开发 分享在企业中积累的实用技能和经验。 通过具体的编码过程、代码示例、步骤详解、核心内容和展示的方法解决遇到的实际问题。 阅读前声明 本系列文章属于付费内容 禁止…...
C++学习:类和对象(二)
一、默认成员函数 1. 什么是默认成员函数? 在C中,每个类都有一些特殊的成员函数,如果程序员没有显式地声明,编译器会自动为类生成这些函数,这些函数称为默认成员函数 2. 默认成员函数列表 默认构造函数(…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...