一文读懂全连接队列(全连接写法)
suiw9 2024-11-17 01:39 20 浏览 0 评论
简介
我们在编写服务端程序时,总是需要先 listen 一下。listen 最重要的就是初始化全连接和半连接队列,本文通过 Java 程序来验证全连接队列溢出情况。
全连接队列
上图是三次握手和网络交互的流程图,通过调用 accept 从全连接队列中获取 socket 进行读写。因此,我们在服务端只 listen 不调用 accept 就可以模拟全连接溢出场景。
队列长度
全连接队列长度与两个参数有关,backlog 和 somaxconn。
backlog 可以通过程序指定,在 ServerSocket 构造函数中,第二个参数 backlog 作用就在这儿。
// https://github.com/torvalds/linux/blob/448b3fe5a0eab5b625a7e15c67c7972169e47ff8/net/socket.c#L1867-L1886
int __sys_listen(int fd, int backlog)
{
struct socket *sock;
int err, fput_needed;
int somaxconn;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
// 获取 net.core.somaxconn,与 backlog 取较小值。
**somaxconn = READ_ONCE(sock_net(sock->sk)->core.sysctl_somaxconn);
if ((unsigned int)backlog > somaxconn)
backlog = somaxconn;**
err = security_socket_listen(sock, backlog);
if (!err)
err = READ_ONCE(sock->ops)->listen(sock, backlog);
fput_light(sock->file, fput_needed);
}
return err;
}
第二个参数在 Linux 中,可以修改 /etc/sysctl.conf 文件。在我的机器上该值为 4096。
动手实践
根据上面的判断,可以推断出下面程序的全连接队列的长度为:min(1, 4096) = 1
/**
* 全连接队列溢出
*/
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8888, 1);
System.out.println("Started on 8888!");
Thread.currentThread().join();
}
}
/**
* 全连接队列溢出
*/
public class Client {
public static void main(String[] args) throws Exception {
System.out.println("Started on: " + System.currentTimeMillis());
try {
for (int i = 0; i < 10; i++) {
Socket socket = new Socket("127.0.0.1", 8888);
System.out.println(socket.getPort());
}
} finally {
System.out.println("End on: " + System.currentTimeMillis());
}
}
}
Linux 5.10 版本
当我们分别执行 java Server 和 java Client 后,在 client 的控制台会打印两次 8888。
Started on: 1715755832881
8888
8888
End on: 1715755963822
Exception in thread "main" java.net.ConnectException: Connection timed out (Connection timed out)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:607)
at java.net.Socket.connect(Socket.java:556)
at java.net.Socket.<init>(Socket.java:452)
at java.net.Socket.<init>(Socket.java:229)
at Client.main(Client.java:13)
通过 ss 查看全连接队列情况时,可以看出 Send-Q(全连接队列最大长度) 值为 1,Recv-Q(全连接队列实际长度) 值为 2。
为什么实际长度要比最大长度大呢? 根据 Linux 作者描述,全连接队列下标是从零开始的,因此连接数要比指定数字多一个。参考:[NET]: Revert incorrect accept queue backlog changes. · torvalds/linux@64a1465 (github.com)
备注:MacOS 上两个长度一致,都为 1。
抓包验证
通过 tcpdump -i lo -w rece.pcap 抓 lo 网卡的包,执行上面的程序。
可以发现在进行过前两次成功的三次握手后,第三次 56128 向 8888 发送 syn 包,但 8888 没有响应,56128 进行了六次重传,最后重传 syn 包失败,程序退出,已经建立成功的连接向 8888 发送 fin 包,销毁连接。
重试次数由 /proc/sys/net/ipv4/tcp_syn_retries 控制,默认为 6 次。重试策略为指数退避,1、2、4、8、16、32 秒进行重试。
不同的 Linux 版本中,全连接队列满后,行为是不一样的。上述是在 5.10 版本的表现。
在 5.10 版本中,只要是全连接队列满了,就会抛掉 syn 包。
// net/ipv4/tcp_input.c tcp_conn_request
// 需要切换到 5.10 tag
// 如果 accept queue 已经满了
if (sk_acceptq_is_full(sk)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
goto drop;
}
Linux 3.10 版本
当我们在 3.10 版本测试上面的程序时,现象比较奇怪,抓到的包则更为奇怪。
// 程序输出结果
Started on: 1715928123222
8888
8888
8888
8888
8888
8888
8888
8888
8888
8888
End on: 1715928129242
- 从输出结果看,在全连接队列限制为 1 的前提下建立了 10 个连接
- 抓包显示,建立成功的连接也不止 2 个,并且还有多次 syn 包重传 和 dup ack。
通过 ss -lnt 看出,全连接队列大小确实为 1,连接成功确实为 2,符合我们之前的预计结果。
那么这些「建立成功」的连接是怎么回事儿呢?
答案是 Client 端收到了 syn ack,发送 ack 就认为连接创建成功了。但 Server 端因为全连接队列满了,抛弃掉了部分 ack 包和 syn 包。所以出现了已经建立成功的连接的 Client 重传 ack 包,已经发送 syn-ack 的 Server 重传 syn-ack 包。
那么 Server 端何时丢 syn 包,何时丢 ack 包呢?(这里我们暂时不讨论半连接溢出的情况)
丢弃 syn 包
// net/ipv4/tcp_ip_v4.c
// 需要切换到 3.10 tag
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
goto drop;
}
上面的程序和 5.10 版本的类似,只不过后面多了 inet_csk_reqsk_queue_young(sk) > 1 的判断。含义是半连接队列中刚到达的 syn,但是没有被超时重传 syn-ack 的数量,意义在于衡量 Server 的处理能力。因为正常三次握手都会很快,所以该值通常为 0。但如果 > 1,意味着此时 Server 端的处理能力不足,因此就要抛弃掉 syn 包来减轻 Server 的处理压力。
我们可以通过 systemtap 工具打印 qlen_young 的值,观察丢包情况。
probe kernel.function("tcp_v4_conn_request") {
tcphdr = __get_skb_tcphdr($skb);
dport = __tcp_skb_dport(tcphdr);
if (dport == 8888)
{
// 获取当前时间的秒数和微秒数
sec = gettimeofday_s();
usec = gettimeofday_us() % 1000000;
// 手动格式化时间
min = (day_sec % 3600) / 60;
sec_only = day_sec % 60;
printf("current: %02d:%02d.%06d \n",
min, sec_only, usec);
// 当前 syn 排队队列的大小
syn_qlen = @cast($sk, "struct inet_connection_sock")->icsk_accept_queue->listen_opt->qlen;
// 当前 syn 排队中 young 的大小
syn_young_qlen = @cast($sk, "struct inet_connection_sock")->icsk_accept_queue->listen_opt->qlen_young;
printf("%02d:%02d.%06d syn queue: syn_qlen=%d, syn_young_qlen=%d\n",
min, sec_only, usec,
syn_qlen, syn_young_qlen);
}
}
将上面的程序保存为 rece_request.c,然后调用 stap -g rece_request.c 观察 syn_yyoung_qlen 的值,当其大于 1 时就会触发丢掉 syn 包操作,再数数抓包文件中 syn 重传,这两个值应该是一致的。
丢弃 ack 包
只要是全连接队列满了,就会丢弃 ack 包。
/*
* The three way handshake has completed - we got a valid synack -
* now create the new socket.
*/
// 三次握手最后一次 ack 处理,完事儿
struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst)
{
// 全连接队是否满了,直接 goto 到溢出,不处理 ack 包。
if (sk_acceptq_is_full(sk))
goto exit_overflow;
}
总结
- 我们在调用 listen 时,主要创建了全连接(accept queue)和半连接(syn queue)队列。
- 全连接队列的大小取决于 min(backlog, somaxconn)
- 不同版本对于全连接队列溢出的策略是不同的,但本质都是通过丢弃 Client 请求来保证 Server 的稳定。
- 全连接队列主要供 accept 调用,因此只要使用先进先出的队列结构即可。全连接队列的数据主要来自于半连接,需要根据不同的 Client 获取,因此被设计成查询高效的哈希表结构。
相关推荐
- 俄罗斯的 HTTPS 也要被废了?(俄罗斯网站关闭)
-
发布该推文的ScottHelme是一名黑客,SecurityHeaders和ReportUri的创始人、Pluralsight作者、BBC常驻黑客。他表示,CAs现在似乎正在停止为俄罗斯域名颁发...
- 如何强制所有流量使用 HTTPS一网上用户
-
如何强制所有流量使用HTTPS一网上用户使用.htaccess强制流量到https的最常见方法可能是使用.htaccess重定向请求。.htaccess是一个简单的文本文件,简称为“.h...
- https和http的区别(https和http有何区别)
-
“HTTPS和HTTP都是数据传输的应用层协议,区别在于HTTPS比HTTP安全”。区别在哪里,我们接着往下看:...
- 快码住!带你十分钟搞懂HTTP与HTTPS协议及请求的区别
-
什么是协议?网络协议是计算机之间为了实现网络通信从而达成的一种“约定”或“规则”,正是因为这个“规则”的存在,不同厂商的生产设备、及不同操作系统组成的计算机之间,才可以实现通信。简单来说,计算机与网络...
- 简述HTTPS工作原理(简述https原理,以及与http的区别)
-
https是在http协议的基础上加了一层SSL(由网景公司开发),加密由ssl实现,它的目的是为用户提供对网站服务器的身份认证(需要CA),以至于保护交换数据的隐私和完整性,原理如图示。1、客户端发...
- 21、HTTPS 有几次握手和挥手?HTTPS 的原理什么是(高薪 常问)
-
HTTPS是3次握手和4次挥手,和HTTP是一样的。HTTPS的原理...
- 一次安全可靠的通信——HTTPS原理
-
为什么HTTPS协议就比HTTP安全呢?一次安全可靠的通信应该包含什么东西呢,这篇文章我会尝试讲清楚这些细节。Alice与Bob的通信...
- 为什么有的网站没有使用https(为什么有的网站点不开)
-
有的网站没有使用HTTPS的原因可能涉及多个方面,以下是.com、.top域名的一些见解:服务器性能限制:HTTPS使用公钥加密和私钥解密技术,这要求服务器具备足够的计算能力来处理加解密操作。如果服务...
- HTTPS是什么?加密原理和证书。SSL/TLS握手过程
-
秘钥的产生过程非对称加密...
- 图解HTTPS「转」(图解http 完整版 彩色版 pdf)
-
我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取。所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议。...
- HTTP 和 HTTPS 有何不同?一文带你全面了解
-
随着互联网时代的高速发展,Web服务器和客户端之间的安全通信需求也越来越高。HTTP和HTTPS是两种广泛使用的Web通信协议。本文将介绍HTTP和HTTPS的区别,并探讨为什么HTTPS已成为We...
- HTTP与HTTPS的区别,详细介绍(http与https有什么区别)
-
HTTP与HTTPS介绍超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的...
- 一文让你轻松掌握 HTTPS(https详解)
-
一文让你轻松掌握HTTPS原文作者:UC国际研发泽原写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。...
- 如何在Spring Boot应用程序上启用HTTPS?
-
HTTPS是HTTP的安全版本,旨在提供传输层安全性(TLS)[安全套接字层(SSL)的后继产品],这是地址栏中的挂锁图标,用于在Web服务器和浏览器之间建立加密连接。HTTPS加密每个数据包以安全方...
- 一文彻底搞明白Http以及Https(http0)
-
早期以信息发布为主的Web1.0时代,HTTP已可以满足绝大部分需要。证书费用、服务器的计算资源都比较昂贵,作为HTTP安全扩展的HTTPS,通常只应用在登录、交易等少数环境中。但随着越来越多的重要...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
tinymce 号称富文本编辑器世界第一,大家同意么?
-
- 最近发表
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)