<网络> 通信函数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函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

python中的高阶函数示例详解

《python中的高阶函数示例详解》在Python中,高阶函数是指接受函数作为参数或返回函数作为结果的函数,下面:本文主要介绍python中高阶函数的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录1.定义2.map函数3.filter函数4.reduce函数5.sorted函数6.自定义高阶函数

Python中的sort方法、sorted函数与lambda表达式及用法详解

《Python中的sort方法、sorted函数与lambda表达式及用法详解》文章对比了Python中list.sort()与sorted()函数的区别,指出sort()原地排序返回None,sor... 目录1. sort()方法1.1 sort()方法1.2 基本语法和参数A. reverse参数B.

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

Python实现简单封装网络请求的示例详解

《Python实现简单封装网络请求的示例详解》这篇文章主要为大家详细介绍了Python实现简单封装网络请求的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录安装依赖核心功能说明1. 类与方法概览2.NetHelper类初始化参数3.ApiResponse类属性与方法使用实

Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧

《Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧》本文将通过实际代码示例,深入讲解Python函数的基本用法、返回值特性、全局变量修改以及异常处理技巧,感兴趣的朋友跟随小编一起看看... 目录一、python函数定义与调用1.1 基本函数定义1.2 函数调用二、函数返回值详解2.1 有返

Python Excel 通用筛选函数的实现

《PythonExcel通用筛选函数的实现》本文主要介绍了PythonExcel通用筛选函数的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录案例目的示例数据假定数据来源是字典优化:通用CSV数据处理函数使用说明使用示例注意事项案例目的第一

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1