多线程select并发

多线程select并发

只需要在上面代码的基础上对服务器端做些更改, 主要逻辑如下:

主线程在检测到有新的客户端连接之后, 创建一个子线程完成accept操作, 具体如下:

if(FD_ISSET(lfd, &rdtemp)){
    auto* info = new fdInfo;
    info->fd = lfd;
    info->maxfd = &maxfd;
    info->rdset = &rdset;
    // 创建子线程
    pthread_t tid;
    pthread_create(&tid, nullptr, acceptConn, info);
    pthread_join(tid, nullptr);
}

acceptConn就是任务函数,info是一个自定义的结构体

还有就是在检测到有通信描述符在rdsert集合中被返回时, 在创建一个子线程, 完成通信操作

for (int i = 0; i <= maxfd; ++i) {
    // 判断从监听的文件描述符之后到maxfd这个范围内的文件描述符是否读缓冲区有数据
    if(i != lfd && FD_ISSET(i, &rdtemp)){
        // 创建子线程
        auto* info = new fdInfo;
        info->fd = i;
        info->rdset = &rdset;
        // 创建子线程
        pthread_t tid;
        pthread_create(&tid, nullptr, conmmunication, info);
        pthread_join(tid, nullptr);
    }
}

整体代码如下

//
// Created by 47468 on 2024/1/24.
//
#include "arpa/inet.h"
#include <cstdio>
#include "unistd.h"
#include "iostream"
#include "string"
#include "cctype"
using namespace std;
#include "pthread.h"

struct fdInfo{
    int fd;
    int* maxfd;
    fd_set* rdset;
};

pthread_mutex_t mutex;

void* acceptConn(void* arg){
    // cout << "线程id: " << pthread_self() << endl;
    auto* info = (fdInfo*)arg;
    sockaddr_in cliaddr{};
    int len = sizeof(cliaddr);
    int cfd = accept(info->fd, (struct sockaddr *) &cliaddr, (socklen_t *) (&len));
    char ip[32];
    cout << "有客户端成功连接, 客户端ip: "
         << inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip))
         << ", port: "
         << ntohs(cliaddr.sin_port)
         << endl;

    // 得到了有效的文件描述符
    // 通信的文件描述符添加到读集合
    // 在下一轮select检测的时候, 就能得到缓冲区的状态
    pthread_mutex_lock(&mutex);
    FD_SET(cfd, info->rdset);
    // 更新maxfd
    *info->maxfd = max(*info->maxfd, cfd);
    pthread_mutex_unlock(&mutex);
    // 释放资源
    delete info;
    cout << "accept done" << endl;
    return nullptr;
}

void* conmmunication(void* arg){
    // cout << "线程id: " << pthread_self() << endl;
    auto* info = static_cast<fdInfo*>(arg);
    // 接收数据
    char buf[10] = {0};
    // 一次只能接收10个字节, 客户端一次发送100个字节
    // 一次是接收不完的, 文件描述符对应的读缓冲区中还有数据
    // 下一轮select检测的时候, 内核还会标记这个文件描述符缓冲区有数据 -> 再读一次
    // 	循环会一直持续, 直到缓冲区数据被读完位置
    ssize_t len = read(info->fd, buf, sizeof(buf));
    if(len == 0){
        cout << "客户端断开了连接..." << endl;
        pthread_mutex_lock(&mutex);
        FD_CLR(info->fd, info->rdset);
        pthread_mutex_unlock(&mutex);
        close(info->fd);
        delete info;
        return nullptr;
    }
    else if(len > 0){
        buf[len] = '\0';
        // 收到了数据
        cout << "客户端: " << buf << endl;
        // 数据处理
        for (int j = 0; j < len; ++j) {
            buf[j] = toupper(buf[j]);
        }
        // 发送回去
        write(info->fd, buf, len);
    } else{
        // 异常
        perror("read");
    }
    delete info;
    return nullptr;
}

int main(){
    pthread_mutex_init(&mutex, nullptr);
    // 1. 创建监听的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    // 2. 绑定
    sockaddr_in saddr{};
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;
    int res = bind(lfd, (struct sockaddr *) &saddr, sizeof(saddr));
    if(res == -1){
        perror("bind");
        close(lfd);
        return -1;
    }

    // 3.设置监听
    res = listen(lfd, 128);
    if(res == -1){
        perror("listen");
        close(lfd);
        return -1;
    }

    // 将监听的fd的状态检测委托给内核检测
    int maxfd = lfd;
    // 初始化检测的读集合
    fd_set rdset;
    fd_set rdtemp;
    // 初始化
    FD_ZERO(&rdset);
    // 将监听的lfd设置到检测的读集合中
    FD_SET(lfd, &rdset);
    // 通过select委托内核检测读集合中的文件描述符状态, 检测read缓冲区有没有数据
    // 如果有数据, select解除阻塞返回
    // 应该让内核持续检测
    while (true){
        // 默认阻塞
        // rdset 中是委托内核检测的所有的文件描述符
        pthread_mutex_lock(&mutex);
        rdtemp = rdset;
        pthread_mutex_unlock(&mutex);
//        timeval* time;
//        time->tv_sec = 0;
//        time->tv_usec = 0;
        select(maxfd + 1, &rdtemp, nullptr, nullptr, nullptr);
        // rdset中的数据被内核改写了,
        // 只保留了发生变化的文件描述的标志位上的1,
        // 没变化的改为0
        // 只要rdset中的fd对应的标志位为1 -> 缓冲区有数据了
        // 判断
        // 有没有新连接
        if(FD_ISSET(lfd, &rdtemp)){
            auto* info = new fdInfo;
            info->fd = lfd;
            info->maxfd = &maxfd;
            info->rdset = &rdset;
            // 创建子线程
            pthread_t tid;
            pthread_create(&tid, nullptr, acceptConn, info);
            pthread_join(tid, nullptr);
        }

        // 再检测有没有通信的文件描述符
        for (int i = 0; i <= maxfd; ++i) {
            // 判断从监听的文件描述符之后到maxfd这个范围内的文件描述符是否读缓冲区有数据
            if(i != lfd && FD_ISSET(i, &rdtemp)){
                // 创建子线程
                auto* info = new fdInfo;
                info->fd = i;
                info->rdset = &rdset;
                // 创建子线程
                pthread_t tid;
                pthread_create(&tid, nullptr, conmmunication, info);
                pthread_join(tid, nullptr);
            }
        }

    }
    pthread_mutex_destroy(&mutex);
    return 0;
}

测试如图:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

grid布局,flex布局实现类似响应式布局的效果

一. grid布局 实现代码 <!DOCTYPE html> <html lang"en"><head><style>.box {display: grid;grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); /*自动填充&#xff0c;最小宽度300px*/justify-content: space-between;gap:…

go语言(十六)----tag

package mainimport ("fmt""reflect" )type resume struct {Name string info:"name" doc:"我的名字"Sex string info:"sex" }func findTag(str interface{}) {t : reflect.TypeOf(str).Elem()for i : 0;i < t.NumField…

redis高可用之Sentinel模式

文章目录 前言1. 创建联想主服务器的网络连接2. 创建连向其他Sentinel的命令连接 &#xff08;也就是调度中心的高可用&#xff09;3.选举领头Sentinel 调度中心高可用4.故障转移 选出新的主服务器 redis服务器高可用如何挑选新的主服务器&#xff1f; 总结 前言 Sentinel(哨岗…

【Keil5】keil5修改主题背景颜色--仿VS Code 主题

keil单调的白底主题难免会让人产生视觉疲劳&#xff0c;该工具里有仿VS的黑色主题.当然&#xff0c;如果你觉得这些都不合你心意也可自己制作配色方案。 1、备份 C:\Keil_v5\UV4下的 global.prop文件。 2、下或者复制 我配置的global.prop文件到你的Keil安装路径&#xff0c;比…

什么是护网行动?

护网&#xff0c;也称“网络保护”&#xff0c;是指网络安全人员对企业或组织的网络进行检查、维护和保护&#xff0c;以防止网络受到黑客攻击、病毒、木马或其它恶意程序的侵入和损害。护网工作包括&#xff1a;网络安全规划、网络配置和控制、漏洞发现和修复、入侵检测和防范…

jenkins 使用webhooks 触发构建任务gitea为例 gitlab类似

jenkins 使用webhooks 触发构建任务gitea为例 多次尝试以及网上的各种资料查阅成功了下 其他人给的答案他们有他们的道理&#xff0c;至少我没有实验成功。我这里就记录下 添加API token 将生成的token 复制保存 第4步骤的时候要用 配置构建方式 填写身份验证令牌 安全配…

Spring复习-问题回答

1.什么是 spring&#xff0c;你对 spring 的理解? Spring是一个轻量级&#xff0c;非侵入式的&#xff08;不使用框架特定的类&#xff0c;感受不到框架&#xff09;IOC和AOP一站式的java后端开发框架&#xff0c;简化企业开发。 2.spring 的优缺点 优点&#xff1a; Spr…

HTML标签(二)

目录 表格标签 表格的主要作用 表格的具体用法 表头单元格标签 表格属性 表格结构标签 合并单元格 合并单元格的方式&#xff1a; 跨行合并&#xff1a; 跨列合并&#xff1a; 列表标签 无序列表 有序列表 自定义列表 表单标签 表单域 表单域的常用属性 表单元素…

阿里云幻兽帕鲁服务器租用价格表,免费?

幻兽帕鲁异常火爆自建幻兽帕鲁服务器不卡又稳定&#xff0c;继腾讯云推出幻兽帕鲁自建服务器教程和4核16G幻兽帕鲁专用特价游戏服务器后&#xff0c;阿里云坐不住了&#xff0c;直接推出特价4核32G和4核16G的palworld专属游戏机&#xff0c;另外还可以申请免费3个月的4核8G无影…

系统移植,GNU命令,Uboot移植

一.GNU命令 1、addr2line 把程序地址转换为文件名和行号 做调试 2、ar 建立&#xff0c;修改&#xff0c;提取归档文件 3、Id:GNU arm-none-linux-gnueabi-ld start.o main.o -Tmap.lds -o uart.elf 链接器 4、as 主要用来编译GNU编译器gcc输出的汇编文件&a…

git本地分支的合并/切换分支时遇到的问题

目录 第一章、本地分支的切换测试1.1&#xff09;切换之前的master分支下文件内容1.2&#xff09;切换到develop分支后修改文件1.3&#xff09;切回master分支出现报错&#xff1a; 第二章、解决方式2.1&#xff09;方式1&#xff1a;commit提交修改2.2&#xff09;方式2&#…

电脑文件mfc140.dll丢失的解决方法指导,怎么快速修复mfc140.dll

mfc140.dll 文件的缺失是个普遍的问题&#xff0c;在日常使用中可能会时不时遇到。本文主要目的是详细介绍一旦遇到 mfc140.dll 文件缺失&#xff0c;应该如何进行下载和安装的步骤。不再赘言&#xff0c;下面就一起深入了解mfc140.dll丢失的解决方法指导。 一. mfc140.dll的作…

VBA即用型代码手册之改变主窗口标题栏名称及隐藏工作表

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。可以大大提高自己的劳动效率&#xff0c;而且可以提高数据的准确性。我这里专注VBA,将我多年的经验汇集在VBA系列九套教程中。 作为我的学员要利用我的积木编程思想&#xff0c;积木编程最重要的是积木如何搭建…

IMU用于室内定位

在室内环境中&#xff0c;全球定位系统&#xff08;GPS&#xff09;的信号受限&#xff0c;因此&#xff0c;开发高精度的室内定位技术成为了研究的热点。近日&#xff0c;来自印度的研究团队采用了粒子滤波算法和多传感器融合技术&#xff0c;探讨了IMU和UWB测量数据的融合&am…

Redis解决方案:NOAUTH Authentication required(连接jedis绑定密码或修改redis密码)

Redis解决方案&#xff1a;NOAUTH Authentication required&#xff08;连接jedis绑定密码或修改redis密码&#xff09; Java使用jedis连接redis时出现错误NOAUTH Authentication required 一、问题报错和原因 本地设置了redis的密码&#xff0c;但在远程连接时并没有输入密…

web系统架构基于springCloud的各技术栈

博主目前开发的web系统架构是基于springCloud的一套微服务架构。 使用的技术栈&#xff1a;springbootmysqlclickhousepostgresqlredisrocketMqosseurekabase-gatewayapollodockernginxvue的一套web架构。 一、springboot3.0 特性&#xff1a;Spring Boot 3.0提供了许多新特性…

绘图软件Visio入门必备!Visio版本|下载|替代软件|模具图库|使用技巧

Visio是什么软件&#xff1f; Visio&#xff0c;全称为Microsoft Visio&#xff0c;是微软旗下的一款图表和矢量图形应用程序&#xff0c;属于Microsoft 365系列的一部分。Visio最初是由前美国软件公司Visio Corporation于1992年推出&#xff0c;微软于2000年收购了Visio Corp…

【华为 ICT HCIA eNSP 习题汇总】——题目集6

1、IEEE 802.11g 标准支持的最大协商速率为&#xff08;&#xff09;。 A、300Mbps B、150Mbps C、54Mbps D、1200Mbps 考点&#xff1a;无线局域网 解析&#xff1a;&#xff08;C&#xff09; IEEE 802.11系列标准如下表&#xff1a; 标准数据传输速率主要技术IEEE 802.111M…

20240124在GoogleEarth中输入GPS坐标查看位置

20240124在GoogleEarth中输入GPS坐标查看位置 2024/1/24 18:08 缘起&#xff1a;斯蒂夫●霍金邀请来自未来的时间旅行者赴宴&#xff01; 有空我也想过去围观一下&#xff01; 于是好奇地找了一下这个地方&#xff01; https://movie.douban.com/subject// 与霍金一起了解宇宙…

7、机器学习中的数据泄露(Data Leakage)

找到并修复这个以微妙的方式破坏你的模型的问题。 数据泄露这个概念在kaggle算法竞赛中经常被提到,这个不同于我们通常说的生活中隐私数据暴露,而是在竞赛中经常出现某支队伍靠着对极个别feature的充分利用,立即将对手超越,成功霸占冠军位置,而且与第二名的差距远超第二名…