当前位置: 首页 > news >正文

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

相关文章:

TCP并发服务器的实现

一请求一线程 问题 当客户端数量较多时&#xff0c;使用单独线程为每个客户端处理请求可能导致系统资源的消耗过大和性能瓶颈。 资源消耗&#xff1a; 线程创建和管理开销&#xff1a;每个线程都有其创建和销毁的开销&#xff0c;特别是在高并发环境中&#xff0c;这种开销…...

前端大屏自适应方案

一般后台管理页面&#xff0c;需要自适应的也就是大屏这一个&#xff0c;其他的尺寸我感觉用第三方框架继承好的就挺合适的&#xff0c;当然自适应方案也可以同步到所有页面&#xff0c;但我感觉除了 to c 的项目&#xff0c;不太需要所有页面自适应&#xff0c;毕竟都是查看和…...

16.3 k8s容器cpu内存告警指标与资源request和limit

本节重点介绍 : Guaranteed的pod Qos最高在生产环境中&#xff0c;如何设置 Kubernetes 的 Limit 和 Request 对于优化应用程序和集群性能至关重要。对于 CPU&#xff0c;如果 pod 中服务使用 CPU 超过设置的limits&#xff0c;pod 不会被 kill 掉但会被限制。如果没有设置 li…...

【计算机网络 - 基础问题】每日 3 题(二十)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…...

铰链损失函数

铰链损失函数&#xff08;Hinge Loss&#xff09;主要用于支持向量机&#xff08;SVM&#xff09;中&#xff0c;旨在最大化分类间隔。它的公式为&#xff1a; L ( y , f ( x ) ) max ⁡ ( 0 , 1 − y ⋅ f ( x ) ) L(y, f(x)) \max(0, 1 - y \cdot f(x)) L(y,f(x))max(0,1−…...

项目实战bug修复

实操bug修复记录 左侧侧边栏切换&#xff0c;再次切换侧边栏&#xff0c;右侧未从顶部初始位置展示。地图定位展示&#xff0c;可跳转到设置的对应位置。一个页面多个el-dialog弹出框导致渲染层级出现问题。锚点滚动定位错位问题。动态类名绑定。el-tree树形通过 draggable 属性…...

Git常用指令整理【新手入门级】【by慕羽】

Git 是一个分布式版本控制系统&#xff0c;主要用于跟踪和管理源代码的更改。它允许多名开发者协作&#xff0c;同时提供了强大的功能来管理项目的历史记录和不同版本。本文主要记录和整理&#xff0c;个人理解的Git相关的一些指令和用法 文章目录 一、git安装 & 创建git仓…...

记某学校小程序漏洞挖掘

前言&#xff1a; 遇到一个学校小程序的站点&#xff0c;只在前端登录口做了校验&#xff0c;后端没有任何校验&#xff0c;奇葩弱口令离谱进去&#xff0c;站点里面越权泄露敏感信息&#xff0c;接管账号等漏洞&#xff01;&#xff01;&#xff01; 渗透思路 1.绕过前端 …...

腾讯百度阿里华为常见算法面试题TOP100(3):链表、栈、特殊技巧

之前总结过字节跳动TOP50算法面试题: 字节跳动常见算法面试题top50整理_沉迷单车的追风少年-CSDN博客_字节算法面试题 链表 160.相交链表...

Apache CVE-2021-41773 漏洞复现

1.打开环境 docker pull blueteamsteve/cve-2021-41773:no-cgid docker run -d -p 8080:80 97308de4753d 2.访问靶场 3.使用poc curl http://47.121.191.208:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd 4.工具验证...

vue-入门速通

setup是最早的生命周期&#xff0c;在vue2里边的data域可以使用this调用setup里面的数据&#xff0c;但是在setup里边不能使用thisvue项目的可执行文件是index&#xff0c;另外运行前端需要npm run vue的三个模块内需要三个不同的结构&#xff0c;里边放置js代码&#xff0c;注…...

【AI大模型】通义大模型API接口实现

目录 一、基础环境安装 &#xff08;一&#xff09;OpenAI Python SDK安装 &#xff08;二&#xff09;DashScope SDK安装 二、OPENAI接口实现 &#xff08;一&#xff09;文本输入 &#xff08;二&#xff09;流式输出 &#xff08;三&#xff09;图像输入 &#xff0…...

CVPR最牛图像评价算法!

本文所涉及所有资源均在 传知代码平台可获取。 目录 概述 一、论文思路 1.多任务学习框架&#xff1a; 2.视觉-语言对应关系&#xff1a; 3.动态损失权重&#xff1a; 4.模型优化和评估&#xff1a; 二、模型介绍 三、详细实现方法 1.图像编码器和语言编码器&#xff08;Image…...

Spring源码-从源码层面讲解传播特性

传播特性:service&#xff1a;REQUIRED&#xff0c;dao:REQUIRED 两个都是required使用的是同一个事务&#xff0c;正常情况&#xff0c;在service提交commit <tx:advice id"myAdvice" transaction-manager"transactionManager"><tx:attributes&…...

Rust调用tree-sitter解析C语言

文章目录 一、Rust 调用 tree-sitter 解析 C 语言代码1. 设置 Rust 项目2. 添加 tree-sitter 依赖3. 编写 Rust 代码4. 运行程序5. 编译出错 二、解决步骤1. 添加 tree-sitter 构建依赖2. 添加 tree-sitter-c 源代码3. 修改 build.rs 以编译 tree-sitter-c 库4. 修改 Cargo.tom…...

奇瑞汽车—经纬恒润 供应链技术共创交流日 成功举办

2024年9月12日&#xff0c;奇瑞汽车—经纬恒润技术交流日在安徽省芜湖市奇瑞总部成功举办。此次盛会标志着经纬恒润与奇瑞汽车再次携手&#xff0c;深入探索汽车智能化新技术的前沿趋势&#xff0c;共同开启面向未来的价值服务与产品新篇章。 面对全球汽车智能化浪潮与产业变革…...

vue3 TagInput 实现

效果 要实现类似于下面这种效果 大致原理 其实是很简单的,我们可以利用 element-plus 组件库里的 el-tag 组件来实现 这里我们可以将其抽离成一个公共的组件,那么现在有一个问题就是通讯问题 这里我们可以利用父子组件之间的通讯,利用 v-model 来实现,父组件传值,子组…...

mysql中的json查询

首先来构造数据 查询department里面name等于研发部的数据 查询语句跟普通的sql语句差不多&#xff0c;也就是字段名要用到path表达式 select * from user u where u.department->$.name 研发部 模糊查询 select * from user u where u.department->$.name like %研发%…...

Etcd权限认证管理

1 查看是否开启权限认证 ctl auth status 2 开启权限认证 ctl auth enable。开启后每一条命令都要加上用户 --userroot:root(root默认最高权限) 3 创建其他用户 ctl user add user1 --user用户名:密码 4 创建角色 ctl role add testR --user 5 为角色添加权限 ctl role g…...

图文组合商标部分驳回后优化后初审通过!

这几天以前有个企业的商标初审下来了&#xff0c;以前是加了图形个别部分没有通过初审&#xff0c;后面是把图形去掉重新用文字申请下来初审。 图形与文字同时申请&#xff0c;会分别审查有一个元素过不了&#xff0c;整体就会过不了&#xff0c;所以平常就会建议分开申请注册商…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

Unity VR/MR开发-VR开发与传统3D开发的差异

视频讲解链接&#xff1a;【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…...