TCP并发服务器的实现

一请求一线程

问题

当客户端数量较多时,使用单独线程为每个客户端处理请求可能导致系统资源的消耗过大和性能瓶颈。

资源消耗:
  • 线程创建和管理开销:每个线程都有其创建和销毁的开销,特别是在高并发环境中,这种开销会显著增加。
  • 内存消耗:每个线程通常需要分配一定的栈空间,这会增加内存使用量。
  • 上下文切换:操作系统需要频繁地切换线程上下文,这会消耗CPU资源。
性能瓶颈:
  • 线程竞争:大量线程会导致线程之间竞争共享资源,如内存和CPU时间,降低整体性能。
  • 调度开销:操作系统调度大量线程时的开销可能会影响应用程序的响应时间和吞吐量。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>#define BUFFER_LENGTH 1024// 客户端处理线程的例程
void *client_routine(void* arg) {int clientfd = *(int*)arg;  // 获取传入的客户端套接字描述符while (1) {char buffer[BUFFER_LENGTH];  // 定义接收缓冲区int len = recv(clientfd, buffer, BUFFER_LENGTH, 0);  // 接收数据if (len < 0) {// 接收数据出错perror("recv error");close(clientfd);  // 关闭客户端套接字break;} else if (len == 0) {// 客户端关闭连接close(clientfd);  // 关闭客户端套接字break;} else {// 打印接收到的数据printf("Recv: %s, %d byte(s)\n", buffer, len);}}return NULL;
}int main(int argc, char* argv[]) {if (argc < 2) {// 参数错误,未提供端口号printf("usage: %s port\n", basename(argv[0]));return -1;}int port = atoi(argv[1]);  // 从命令行参数获取端口号// 创建监听用的套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket creation failed");return 1;}// 配置套接字地址struct sockaddr_in addr;memset(&addr, 0, sizeof(struct sockaddr_in));  // 清空地址结构addr.sin_family = AF_INET;addr.sin_port = htons(port);  // 转换端口号为网络字节序addr.sin_addr.s_addr = INADDR_ANY;  // 绑定到所有可用的接口if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))) {perror("bind failed");return 2;}if (listen(sockfd, 5) < 0) {perror("listen failed");return 3;}while (1) {struct sockaddr_in client_addr;memset(&client_addr, 0, sizeof(struct sockaddr_in));  // 清空客户端地址结构socklen_t client_len = sizeof(client_addr);// 接受客户端连接int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);if (clientfd < 0) {perror("accept failed");continue;}// 为每个客户端创建一个线程pthread_t thread_id;if (pthread_create(&thread_id, NULL, client_routine, &clientfd) != 0) {perror("pthread_create failed");close(clientfd);  // 创建线程失败时关闭客户端套接字}// 可选:分离线程以避免线程资源泄漏pthread_detach(thread_id);}// 关闭监听套接字(实际上这部分代码永远不会到达)close(sockfd);return 0;
}

使用ifconfig查看服务器程序所在主机的IP地址。

首先启动所写的tcp服务器,即确保tcp_server_test.cpp已经编译并运行在虚拟机上,监听指定的端口(8888)。

打开三个网络调试助手(NetAssist),在每个助手中配置远端主机地址为你的tcp服务器地址(在虚拟机用ifconfig查看),端口设置为 8888,点击连接。可以分别向tcp服务器写数据。

利用epoll

优点:

高效:

epoll采用事件驱动的方式,仅在有事件发生时通知应用程序,避免了轮询带来的性能开销。

可扩展性

能够处理大量的文件描述符,适合高并发应用。

边缘触发

支持边缘触发(EPOLLET),在数据到达时通知一次,适合需要高效处理大量事件的场景。

缺点

复杂性

编程模型较为复杂,需要正确处理事件并维持数据流动性,可能导致代码较难维护。

资源消耗

虽然epoll高效,但在高负载情况下,资源使用仍然会增加,如内存和系统调用次数。

边缘触发处理

需要确保处理所有数据,否则可能错过事件,增加了编程的复杂性。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>#define BUFFER_LENGTH 1024
#define EPOLL_SIZE 1024int main(int argc, char* argv[]) {if (argc < 2) {// 参数错误,未提供端口号printf("usage: %s port\n", basename(argv[0]));return -1;}int port = atoi(argv[1]);  // 从命令行参数获取端口号// 创建监听用的套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket creation failed");return 1;}// 配置套接字地址struct sockaddr_in addr;memset(&addr, 0, sizeof(struct sockaddr_in));  // 清空地址结构addr.sin_family = AF_INET;addr.sin_port = htons(port);  // 转换端口号为网络字节序addr.sin_addr.s_addr = INADDR_ANY;  // 绑定到所有可用的接口if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))) {perror("bind failed");close(sockfd);return 2;}if (listen(sockfd, 5) < 0) {perror("listen failed");close(sockfd);return 3;}// 创建 epoll 实例int epfd = epoll_create1(0);  // 使用 epoll_create1(0) 代替 epoll_create(0)if (epfd < 0) {perror("epoll_create failed");close(sockfd);return 4;}struct epoll_event events[EPOLL_SIZE] = {0};// 添加监听套接字到 epoll 实例struct epoll_event ev;ev.events = EPOLLIN | EPOLLET;  // 设置为边缘触发模式ev.data.fd = sockfd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) < 0) {perror("epoll_ctl failed");close(sockfd);close(epfd);return 5;}while (1) {// 等待事件发生int nready = epoll_wait(epfd, events, EPOLL_SIZE, -1);if (nready < 0) {perror("epoll_wait failed");break;  // 退出循环}for (int i = 0; i < nready; i++) {if (events[i].data.fd == sockfd) {struct sockaddr_in client_addr;memset(&client_addr, 0, sizeof(struct sockaddr_in));  // 清空客户端地址结构socklen_t client_len = sizeof(client_addr);// 接受客户端连接int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);if (clientfd < 0) {perror("accept failed");continue;}// 将新的客户端套接字添加到 epoll 实例中,并设置为边缘触发模式ev.events = EPOLLIN | EPOLLET;ev.data.fd = clientfd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev) < 0) {perror("epoll_ctl failed");close(clientfd);}} else {// 处理客户端套接字的事件int clientfd = events[i].data.fd;char buffer[BUFFER_LENGTH];  // 定义接收缓冲区int len;// 处理所有可用的数据while ((len = recv(clientfd, buffer, BUFFER_LENGTH, 0)) > 0) {buffer[len] = '\0';  // 添加字符串结束标志printf("Recv: %s, %d byte(s)\n", buffer, len);}if (len < 0) {perror("recv error");}// 客户端关闭连接或出错close(clientfd);  // 关闭客户端套接字epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, NULL);}}}// 关闭监听套接字和 epoll 实例close(sockfd);close(epfd);return 0;
}

推荐一下 

0voice · GitHub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/426354.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

性能测试的复习3-jmeter的断言、参数化、提取器

一、断言、参数化、提取器 需求&#xff1a; 提取查天气获取城市名请求的响应结果&#xff1a;城市对查天气获取城市名的响应结果进行响应断言和json断言对查天气获取城市名添加用户参数 1、步骤 查看天气获取城市名 json提取器&#xff08;对响应结果提取、另一个接口请求…

简单了解微服务--黑马(在更)

认识微服务 单体架构 不适合大型复杂项目 微服务架构 将单体结构的各个功能模块拆分为多个独立的项目 拆取的独立项目分别开发&#xff0c;在部署的时候也要分别去编译打包&#xff0c;分别去部署&#xff0c;不同的模块部署在不同的服务器上&#xff0c;对外提供不同的功能…

小间距LED显示屏的技术原理分析

在现代显示技术领域&#xff0c;小间距LED显示屏以其卓越的显示效果和灵活的应用场景&#xff0c;逐渐成为市场的新宠。本文将深入探讨小间距LED显示屏的技术原理&#xff0c;分析其在显示领域的应用优势。 A、小间距LED显示屏的基本概念 小间距LED显示屏是指LED灯珠之间的间距…

linux hadoop-3.3.6 hbase-2.5.7

软件下载 hadoop https://dlcdn.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz 可以直接下载到本地&#xff0c;也可以直接下载进虚拟机中 如果速度较慢&#xff0c;可以用&#xff1b;另一个 wget https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common…

spring-boot-maven-plugin插件打包和java -jar命令执行原理

文章目录 1. Maven生命周期2. jar包结构2.1 不可执jar包结构2.2 可执行jar包结构 3. spring-boot-maven-plugin插件打包4. 执行jar原理 1. Maven生命周期 Maven的生命周期有三种&#xff1a; clean&#xff1a;清除项目构建数据&#xff0c;较为简单&#xff0c;不深入探讨&a…

spring容器创建bean过程中使用到的几个factory

文章目录 前述BeanFactoryFactoryBeanObjectFactory 前述 spring我们可以理解为一个帮我们管理bean的容器&#xff0c;使用spring框架之前创建bean都是通过new的方式&#xff0c;使用spring框架之后&#xff0c; 我们只需要告诉spring框架我们有那些bean&#xff0c;它会帮我们…

k8s证书过期处理

证书一共分为 根CA&#xff08;ca.crt&#xff09; master各组件的证书&#xff08;包括etcd、apiserver、front-proxy、controller-manager等各种&#xff09; kubelet证书 k8s证书有效期说明&#xff1a; 1、原生版本有效期master节点&#xff1a; /etc/kubernetes/ssl/…

YOLOv10改进系列,YOLOv10损失函数更换为Powerful-IoU(2024年最新IOU),助力高效涨点

改进前训练结果: 改进后的结果: 摘要 边界框回归(BBR)是目标检测中的核心任务之一,BBR损失函数显著影响其性能。然而,观察到现有基于IoU的损失函数存在不合理的惩罚因子,导致回归过程中锚框扩展,并显著减缓收敛速度。为了解决这个问题,深入分析了锚框扩展的原因。针…

基于 K8S kubernetes 的常见日志收集方案

目录 1、日志对我们来说到底重不重要&#xff1f; 2、常见的日志收集方案 2.1 EFK 2.2 ELK Stack 2.3 ELKfilebeat 2.4 其他方案 2、elasticsearch组件介绍 3、filebeat组件介绍 3.1 filebeat和beat关系 3.2 filebeat是什么&#xff1f; 3.3 Filebeat工作原理 3.4 …

ELFK日志分析平台,架构和通信

整个架构&#xff0c;加上跳板机&#xff0c;总共12台机器 技术方案&#xff1a; 1. 配置nfs服务器&#xff0c;为web集群提供共享网络文件系统 # 部署 NFS 服务 [rootnfs ~]# dnf install -y nfs-utils [rootnfs ~]# vim /etc/exports /var/webroot 192.168.1.0/24(rw,…

xml重点笔记(尚学堂 3h)

XML:可扩展标记语言 主要内容(了解即可) 1.XML介绍 2.DTD 3.XSD 4.DOM解析 6.SAX解析 学习目标 一. XML介绍 1.简介 XML(Extensible Markup Language) 可扩展标记语言&#xff0c;严格区分大小写 2.XML和HTML XML是用来传输和存储数据的。 XML多用在框架的配置文件…

剖析 MySQL 数据库连接池(C++版)

目录 ☀️0. 前言 &#x1f324;️1. 数据库连接池概述 ⛅1.1 服务器与数据库交互 ⛅1.2 MySQL 数据库网络模型 ⛅1.3 MySQL 连接驱动安装 ⛅1.4 同步&#xff08;synchronous&#xff09;连接池与异步&#xff08;asynchronous&#xff09;连接池 ⛅1.5 同步连接池和异…

记录开发一个英语听力训练网站

背景 在当前全球经济衰退的背景下&#xff0c;IT相关的工作在国内的竞争也是越来越激烈&#xff0c;为了能够获得更多的可能性&#xff0c;英语的学习也许能为程序员打开一扇新的窗户&#xff0c;比如很多远程的工作尤其是国际化背景的工作团队&#xff0c;英语的协作沟通是必…

yolov8-obb中存在的一个bug

yolov8支持OBB目标检测,且能提供较好的性能。 但是最近在使用yolov8-obb的过程中,发现yolov8-obb存在一个bug。即训练数据如果包含不带旋转角度的水平目标时,训练出的模型,经常会输出垂直的检测框,需要旋转90度以后才能得到最终结果。把yolov8-obb相关的源码阅读一遍才发…

初始爬虫5

响应码&#xff1a; 数据处理&#xff1a; re模块&#xff08;正则表达式&#xff09; re模块是Python中用于正则表达式操作的标准库。它提供了一些功能强大的方法来执行模式匹配和文本处理。以下是re模块的一些常见用法及其详细说明&#xff1a; 1. 基本用法 1.1 匹配模式 …

STM32 的 RTC(实时时钟)详解

目录 一、引言 二、RTC 概述 三、RTC 的工作原理 1.时钟源 2.计数器 3.闹钟功能 4.备份寄存器 四、RTC 寄存器 1.RTC_TR&#xff08;Time Register&#xff0c;时间寄存器&#xff09; 2.RTC_DR&#xff08;Date Register&#xff0c;日期寄存器&#xff09; 3.RTC_S…

TCP 拥塞控制:一场网络数据的交通故事

从前有条“高速公路”&#xff0c;我们叫它互联网&#xff0c;而这条公路上的车辆&#xff0c;则是数据包。你可以把 TCP&#xff08;传输控制协议&#xff09;想象成一位交通警察&#xff0c;负责管理这些车辆的行驶速度&#xff0c;以防止交通堵塞——也就是网络拥塞。 第一…

【MPC】无人机模型预测控制复现Data-Driven MPC for Quadrotors项目(Part 1)

无人机模型预测控制复现Data-Driven MPC for Quadrotors项目 参考链接背景和问题方法与贡献实验结果安装ROS创建工作空间下载RotorS仿真器源码和依赖创建Python虚拟环境下载data_driven_mpc仓库代码下载并配置ACADO求解器下载并配置ACADO求解器的Python接口下载并配置rpg_quadr…

基于密码的大模型安全治理的思考

文章目录 前言一、大模型发展现状1.1 大模型技术的发展历程1.2 大模型技术的产业发展二、大模型安全政策与标准现状2.1 国外大模型安全政策与标准2.2 我国大模型安全政策与标准前言 随着大模型技术的迅速发展和广泛应用,其安全性问题日益凸显。密码学作为网络空间安全的核心技…

如何简化机器人模型,加速仿真计算与可视化

通常,我们希望将自己设计的机器人模型导入仿真环境。由于是通过 CAD 软件设计的,导出的 urdf 使用 STL 或 DAE 文件来表示 3D 几何。但原始的 STL 或 DAE 文件通常过于复杂(由数十万个三角面片组成),这会减慢仿真速度,有时也会导致仿真软件报错(如Webots)。为了在正确描述…