<网络> 通信函数listen的第二个参数

2024-04-29 15:36

本文主要是介绍<网络> 通信函数listen的第二个参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言:

一、实验

二、listen 函数的第二个参数

(一)未完成连接队列和全连接队列

(二)为什么 TCP 中要维护连接队列?

(三)为什么连接队列的长度不能太长?

(四)全连接队列的长度决定因素


前言:

在编写 TCP 套接字跟 HTTP 的服务器代码时,我们使用 listen 函数将套接字设置为监听状态,等待客户端的连接请求。其中 listen 函数的第一个参数就是需要设置为监听状态的套接字,而 listen 函数的第二个参数我们一般将其设置为32,该参数常常被忽略或者不完全理解,那么该参数具体是什么含义呢?接下来通过实验来理解一下该参数。

一、实验

下面我们通过一个简单的实验来说明一下 listen 函数的第二个参数的含义

<网络> HTTP-CSDN博客 见一见请求 小节中:

  • 将 listen 的第二个参数值设置为2,即 default_port
  • 服务器初始化完成之后,先不调用 accept 函数获取底层连接,服务器启动就一个死循环。
#pragma once#include "Sock.hpp"#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>class HttpServer;class ThreadData
{
public:ThreadData(int sock, const uint16_t &port, const string &ip, HttpServer *httpsvr):sock_(sock), port_(port), ip_(ip), httpsvr_(httpsvr){}~ThreadData(){if(sock_>=0) close(sock_);}
public:int sock_;uint16_t port_;string ip_;HttpServer *httpsvr_;
};class HttpServer
{static const uint16_t default_port = 2;using func_t = function<string(const string &)>;
public: HttpServer(func_t func, uint16_t port = default_port): func_(func), _port(port){}void Init(){listensock_.Socket();listensock_.Bind(_port);listensock_.Listen(); // 封装的listen函数此时为2logMessage(Debug, "Init server success");}void Start(){while(true){}}static void *threadRoutine(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData*>(args);// 假设一次都读完了char buff[4096];std::string request;ssize_t s = recv(td->sock_, buff, sizeof(buff)-1, 0);if(s > 0){buff[s] = 0;request = buff;string response = td->httpsvr_->func_(request);send(td->sock_, response.c_str(), response.size(), 0);}else{logMessage(Debug, "Cilent [%d -> %s:%d] Quit", td->sock_, td->ip_.c_str(), td->port_);}delete td;return nullptr;}~HttpServer(){listensock_.Close();}
private:uint16_t _port;Sock listensock_;func_t func_;
};

运行该服务程序,使用 netstat -nltp 命令查看当前 TCP 网络协议,可以看出当前服务正处于监听状态:

接下来,我们依次 telnet 127.0.0.1 8081 四次向该服务发起连接请求,通过命令 sudo netstat -ntp | head -2 && sudo netstat -ntp | grep 8081 查看该服务器的连接数。如上所示,向服务器发送了四次请求,前三次请求处于 ESTABLISHED 状态,而第四次再次向服务发起连接时,服务器新增了一个状态为 SYN_RCVD 的连接:

服务器上面新增了一个状态为 SYN_RCVD 的连接,在<网络> 传输层的TCP/UDP协议-CSDN博客文章中已经清楚说明 SYN_RCVD 表示服务器没有对客户端发来的请求连接进行响应:

对于上面的 SYN_RCVD 状态的连接,由于服务器长时间不对其响应,所以三次握手失败,该连接被自动释放。再一次查询时,该连接消失:

根据以上的实验现象,可发现。在该实验中,无论有多少客户端向服务器发起连接请求,最终在服务器端最多只有三个连接会建立成功。当第四个连接请求到达服务器时,服务器只收到了该客户端发送的 SYN 请求,但并没有对其进行响应,该连接在一段时间之后就会自动关闭了。对于后续的连接请求,服务器会直接拒绝这些请求。我们可以猜测,是与listen函数的第二个参数有关。

二、listen 函数的第二个参数

(一)未完成连接队列和全连接队列

实际上,Linux 内核协议栈为一个 TCP 连接管理使用两个队列:

  • 监听队列(Listen Queue):也称为未完成连接队列(incomplete connection queue)或半连接队列(half-open connect queue)。该队列用于存放服务器正在等待完成三次握手过程的连接请求。当服务器调用 listen 函数将套接字设置为监听状态后,它会开始接受传入的连接请求,并将这些请求放入监听队列中。三次握手过程中,服务器收到客户端的 SYN 请求后,会将该连接请求从监听队列中取出并处理。
  • 已完成连接队列(Completed Connection Queue):也称为全连接队列。该队列用于存放已经完成三次握手并建立连接。当服务器成功处理完成连接请求并完成三次握手后,将连接从监听队列移到全连接队列中,表示该连接已经准备好进行数据交换。

全连接队列的长度实际上受到 listen 函数的第二个参数的影响。一般情况下,TCP 全连接队列的长度等于 listen 函数的第二个参数的值+1

在上面实验中,listen 的参数我们设置为2,这意味着服务器的全连接队列的长度为3。因此,服务器最多允许同时存在三个处于 ESTABLISHED 状态的连接。

半连接队列(监听队列)和全连接队列(已完成连接队列)在连接管理中起着不同的作用。半连接队列用于存放未完成连接的请求,而全连接队列用于存放已经建立连接的队列。通过合理设置 listen 函数的第二个参数,可以控制全连接队列的长度,从而限制并发连接数从而保护服务器资源。

(二)为什么 TCP 中要维护连接队列?

在服务器端启动时,服务器预先创建多个线程用于处理客户端的请求。主线程会从底层接受连接并将其交给这些服务线程进行处理。如果向服务器发起连接的客户端较少,那么连接一旦在底层建立好,主线程会立即接受并将其交给服务线程处理。这样就可以快速响应连接请求并提供服务了。

在高负载的环境下,服务器可能会同时收到多个连接请求。如果每个服务线程都在为某个连接提供服务,主线程在这段时间内无法接收新的连接请求。这时,已经建立好的连接就会被放入连接队列中。只有当某个服务线程空闲时,主线程才能从连接队列中获取已建立的连接。

如果没有连接队列,当所有服务线程都在提供服务时,新的连接请求将直接被拒绝,这可能导致一些客户端无法及时建立连接。通过设置连接队列,当某个服务线程完成服务后,如果连接队列中有已建立的连接,主线程可以立即从连接队列中获取一个连接并交给该服务线程处理。这样可以保证服务器几乎是满载工作的状态,最大限度的提供服务能力。

(三)为什么连接队列的长度不能太长?

  • 连接队列需要占用服务器的内存资源来存储连接请求。连接队列过长会占用更多的空间,可能导致服务器的性能下降。且队列中的连接需要等待较长的时间才能得到服务。这会增加客户端的连接建立和响应延迟,降低了用户体验。
  • 连接队列中的连接请求需要维护其状态信息,包括三次握手、超时处理等。如果队列过长,维护这些状态信息需要消耗更多的 CPU 资源和时间。
  • 较长的连接队列可能会增加服务器面临的潜在风险。例如,如果恶意客户端发送大量的连接请求,将连接队列撑满,可能导致服务器无法响应真正的合法连接请求。这可能导致服务器遭受拒绝服务(Denial of Service,DoS)攻击。

因此,为了避免上述问题的出现,连接队列的长度需要适度。通常,系统会设置一个合理的值,不是一个固定的数值,它取决于多种因素,如系统的性能需求、网络拥塞情况、请求处理时间等,以平衡服务器的资源利用和客户端的响应请求。可以根据实际的场景和需要对连接队列长度进行调整。

(四)全连接队列的长度决定因素

全连接队列的长度由以下两个因素决定:

  • 用户层调用 listen 函数时传入的第二个参数 backlog该参数表示在队列中等待被接受的连接的最大数量。具体的取值范围可能会受到操作系统或网络库的限制。
  • 系统变量 net.core.somaxconn这是一个系统变量,用于限制连接队列的最大长度。它表示操作系统允许的最大连接队列长度。该值在不同的操作系统中可能会有所不同,通常默认值为 128

实际上,连接队列长度不一定等于 backlog net.core.somaxconn 的值,而是取两者中的 较小值+1 作为连接队列实际长度。

可以使用命令 sysctl net.core.somaxconn 来查看其的值:

设置合适的连接队列长度,可以通过调整 backlog 参数和 net.core.somaxconn 变量的值,使其适应服务器的负载和性能要求。

这篇关于<网络> 通信函数listen的第二个参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/946483

相关文章

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

postgresql使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数

Python实现MQTT通信的示例代码

《Python实现MQTT通信的示例代码》本文主要介绍了Python实现MQTT通信的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 安装paho-mqtt库‌2. 搭建MQTT代理服务器(Broker)‌‌3. pytho

MySQL字符串常用函数详解

《MySQL字符串常用函数详解》本文给大家介绍MySQL字符串常用函数,本文结合实例代码给大家介绍的非常详细,对大家学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql字符串常用函数一、获取二、大小写转换三、拼接四、截取五、比较、反转、替换六、去空白、填充MySQL字符串常用函数一、

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

MySql基本查询之表的增删查改+聚合函数案例详解

《MySql基本查询之表的增删查改+聚合函数案例详解》本文详解SQL的CURD操作INSERT用于数据插入(单行/多行及冲突处理),SELECT实现数据检索(列选择、条件过滤、排序分页),UPDATE... 目录一、Create1.1 单行数据 + 全列插入1.2 多行数据 + 指定列插入1.3 插入否则更

PostgreSQL中rank()窗口函数实用指南与示例

《PostgreSQL中rank()窗口函数实用指南与示例》在数据分析和数据库管理中,经常需要对数据进行排名操作,PostgreSQL提供了强大的窗口函数rank(),可以方便地对结果集中的行进行排名... 目录一、rank()函数简介二、基础示例:部门内员工薪资排名示例数据排名查询三、高级应用示例1. 每

全面掌握 SQL 中的 DATEDIFF函数及用法最佳实践

《全面掌握SQL中的DATEDIFF函数及用法最佳实践》本文解析DATEDIFF在不同数据库中的差异,强调其边界计算原理,探讨应用场景及陷阱,推荐根据需求选择TIMESTAMPDIFF或inte... 目录1. 核心概念:DATEDIFF 究竟在计算什么?2. 主流数据库中的 DATEDIFF 实现2.1

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Spring Boot spring-boot-maven-plugin 参数配置详解(最新推荐)

《SpringBootspring-boot-maven-plugin参数配置详解(最新推荐)》文章介绍了SpringBootMaven插件的5个核心目标(repackage、run、start... 目录一 spring-boot-maven-plugin 插件的5个Goals二 应用场景1 重新打包应用