<网络> 通信函数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中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

Python中bisect_left 函数实现高效插入与有序列表管理

《Python中bisect_left函数实现高效插入与有序列表管理》Python的bisect_left函数通过二分查找高效定位有序列表插入位置,与bisect_right的区别在于处理重复元素时... 目录一、bisect_left 基本介绍1.1 函数定义1.2 核心功能二、bisect_left 与

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

在Spring Boot中实现HTTPS加密通信及常见问题排查

《在SpringBoot中实现HTTPS加密通信及常见问题排查》HTTPS是HTTP的安全版本,通过SSL/TLS协议为通讯提供加密、身份验证和数据完整性保护,下面通过本文给大家介绍在SpringB... 目录一、HTTPS核心原理1.加密流程概述2.加密技术组合二、证书体系详解1、证书类型对比2. 证书获

Python函数返回多个值的多种方法小结

《Python函数返回多个值的多种方法小结》在Python中,函数通常用于封装一段代码,使其可以重复调用,有时,我们希望一个函数能够返回多个值,Python提供了几种不同的方法来实现这一点,需要的朋友... 目录一、使用元组(Tuple):二、使用列表(list)三、使用字典(Dictionary)四、 使

Linux网络配置之网桥和虚拟网络的配置指南

《Linux网络配置之网桥和虚拟网络的配置指南》这篇文章主要为大家详细介绍了Linux中配置网桥和虚拟网络的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、网桥的配置在linux系统中配置一个新的网桥主要涉及以下几个步骤:1.为yum仓库做准备,安装组件epel-re