<网络> 通信函数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

相关文章

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

SpringMVC获取请求参数的方法

《SpringMVC获取请求参数的方法》:本文主要介绍SpringMVC获取请求参数的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下... 目录1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、@RequestParam4、@

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

MySQL高级查询之JOIN、子查询、窗口函数实际案例

《MySQL高级查询之JOIN、子查询、窗口函数实际案例》:本文主要介绍MySQL高级查询之JOIN、子查询、窗口函数实际案例的相关资料,JOIN用于多表关联查询,子查询用于数据筛选和过滤,窗口函... 目录前言1. JOIN(连接查询)1.1 内连接(INNER JOIN)1.2 左连接(LEFT JOI

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

Spring Boot项目部署命令java -jar的各种参数及作用详解

《SpringBoot项目部署命令java-jar的各种参数及作用详解》:本文主要介绍SpringBoot项目部署命令java-jar的各种参数及作用的相关资料,包括设置内存大小、垃圾回收... 目录前言一、基础命令结构二、常见的 Java 命令参数1. 设置内存大小2. 配置垃圾回收器3. 配置线程栈大小

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

SpringBoot利用@Validated注解优雅实现参数校验

《SpringBoot利用@Validated注解优雅实现参数校验》在开发Web应用时,用户输入的合法性校验是保障系统稳定性的基础,​SpringBoot的@Validated注解提供了一种更优雅的解... 目录​一、为什么需要参数校验二、Validated 的核心用法​1. 基础校验2. php分组校验3

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(