IO多路复用机制——Select

2023-11-04


select函数族如下:

在这里插入图片描述

服务器端使用select机制监听可读的文件描述符(客户端)的一般流程如下:

  1. 执行fd_set myset; 实例化一个fd_set对象。
  2. FD_ZERO(&myset); 把myset的所有位置为0,例如 0000 0000.
  3. 若新连接的客户端的文件描述符为2,用一个变量fd来记录该文件描述符,执行FD_SET(fd , &myset); 使myset的从右往左的第三位置为1,因此myset变为 0010 0000.
  4. 若此时又有两个新客户端连接,例如fd=3、fd=5,则再次调用FD_SET函数使myset变为 0011 0100.
  5. 执行select(6 , &myset ,NULL, NULL , 0); 第一个参数是此时的最大文件描述符再加1,因为我这里步骤4新增的客户端种fd最大为5,所以第一个参数应该填为6;第二个参数是表示用去监听可读文件描述符的fd_set对象,这里就是myset;第三个参数是监听可写文件描述符的fd_set对象,因为我这里只监听可读的文件描述符,所以这里填NULL;第四个参数是监听异常的,这里也不使用,所以填NULL;第五个参数是阻塞的时间,填0表示永久阻塞,知道有某个文件描述符可读,如果不想永久阻塞,可以填入一个stuct timeval 对象。
  6. 执行完select后,myset的所有位被置为0。此时如果只有客户端fd=2发生了可读事件,则第五步中select的返回值大于0,同时myset从左往右第三位被重新置为1( 0010 0000);如果没有客户端文件描述符发生可读事件,同时又阻塞的时间到,则返回值为0;如果返回值小于0,则表示发生了错误。
  7. select返回之后,最后调用FD_ISSET( 2 , &myset); 来判断fd=2 是否可读,也就是判断myset的第三位是否被置为1。如果fd=2可读(也就是myset的第三位被置为1),则 FD_ISSET返回值为 非零,否则则返回0。
  8. 至此select的使用流程结束。

下面将说下 如何将select来实现多并发的双向通信:

1. 首先主要的思想是启动三个线程。
2. 第一个线程用select阻塞并监听客户端的fd,判断该客户端是否可读。
3. 第二个线程从标准输入获取要发送的信息,并将信息发送给各个客户端。
4. 第三个线程是主线程,用来accpet阻塞,时刻监听是否有客户端发起tcp连接请求。

代码如下:

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h> //POSIX 操作系统API
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/poll.h>
#include <ctime>
#include <unordered_set>

using namespace std;

pthread_mutex_t mutex;             //锁
unordered_set<int> Client_Fd_Set;  //存放客户端文件描述符的SET

time_t t = time(0);          //记录发送和接收数据的时间


[[noreturn]] void *recv_thread(void *arg) //接收线程
{
    struct timeval tv;
    tv.tv_sec=10;
    tv.tv_usec=0;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        while(Client_Fd_Set.size()==0);
        fd_set Fd_set;
        FD_ZERO(&Fd_set);
        int max_fd=0;
        unordered_set<int>::iterator it;
        for(it = Client_Fd_Set.begin() ; it!=Client_Fd_Set.end() ;++it)
        {
            FD_SET(*it,&Fd_set);
            max_fd=max(max_fd,*it);
        }
        int res = select(max_fd+1,&Fd_set , NULL ,NULL,&tv);
        if(res<0)
        {
            perror("select error");
        }
        else if(res==0)
        {
            cout<<"no new message"<<endl;
        }
        else
        {
            char rx_buf[1024];
            cout<<"have new message"<<endl;
            for(it=Client_Fd_Set.begin() ; it!=Client_Fd_Set.end() ; ++it)
            {
                if( FD_ISSET(*it,&Fd_set)!=0)
                {
                    memset(rx_buf,0,sizeof(rx_buf));
                    int len = recv(*it,rx_buf,sizeof(rx_buf),0);//最后一个参数可改为MSG_WAITALL,会导致一直阻塞直到读取到指定字节
                    if(len<=0){
                        cout<<"len:"<<len<<endl;
                        close(*it);
                        Client_Fd_Set.erase(*it);
                        close(*(int*)arg);
                        exit(0);
                    }
                    t = time(0);
                    struct tm *timemsg = localtime(&t);
                    string time;
                    time+="(";time+= to_string(timemsg->tm_hour);time+=':';time+=to_string(timemsg->tm_min);time+=':';time+=to_string(timemsg->tm_sec);time+=")";
                    cout<<time<<"recv from "<<*it<<":"<<rx_buf<<endl;
                    if(strcmp(rx_buf,"end")==0)
                    {
                        close(*it);
                        cout<<"Close Client fd:"<<*it<<endl;
                        Client_Fd_Set.erase(*it);
//                        close(*(int*)arg);
                        cout<<"recv_thread exit"<<endl;
                        pthread_exit(0);
                    }
                    //数据回传,将接收到的数据回传给客户端,有需要的话把0改成1
                    #if 0
                    send(*it,rx_buf,len ,0);
                    cout<<time<<"send to "<<*it<<":"<<rx_buf<<endl;
                    #endif
                }
            }
        }
        pthread_mutex_unlock(&mutex);
        sleep(0.01);
    }
}

[[noreturn]] void *send_thread(void *arg)  //发送线程
{
    while(true)
    {
        string temp_buf;
        getline(cin,temp_buf);
        if(temp_buf=="end")             //当标准输入 输入end时,关闭嵌套字,导致主线程里会跳出while
        {
            cout<<"send_thread exit"<<endl;
            close(*(int *)arg);
            pthread_exit(0);
        }
        unordered_set<int>::iterator it;
        for(it = Client_Fd_Set.begin();it!=Client_Fd_Set.end();++it) //这里是往所有客户端发信息,可以再优化
        {
            char *send_buf;
            send_buf = (char *)temp_buf.data();
            send(*it,send_buf,strlen(send_buf) ,0);

            t = time(0);
            struct tm *timemsg = localtime(&t);
            string time;
            time+="(";time+= to_string(timemsg->tm_hour);time+=':';time+=to_string(timemsg->tm_min);time+=':';time+=to_string(timemsg->tm_sec);time+=")";
            cout<<time<<"send to "<<*it<<":"<<send_buf<<endl;
        }
        sleep(0.01);
    }
}


int main() {
    pthread_t thread_write,thread_read;
    std::cout << "Server start!" << std::endl;
    int server_fd = socket(AF_INET, SOCK_STREAM , 0);
    std::cout << "Server fd="<<server_fd << std::endl;
    struct sockaddr_in server_addr , client_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(995);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 字符串转网络字节序
//    server_addr.sin_addr.s_addr = inet_addr("0,0,0,0"); 跟上面一句等价
    if(-1 == bind(server_fd,(struct  sockaddr*)&server_addr ,sizeof(server_addr)))
    {
        perror("bind error");
        exit(1);
    }

    if(-1 == listen(server_fd,20))
    {
        perror("listen error");
        exit(1);
    }
    pthread_mutex_init(&mutex,NULL);
    pthread_create(&thread_write ,NULL , send_thread ,&server_fd);
    pthread_detach(thread_write);
    pthread_create(&thread_read ,NULL , recv_thread ,&server_fd);
    pthread_detach(thread_read);

    socklen_t client_addr_len = sizeof(client_addr);
    while(true)
    {
        int client_fd = accept(server_fd,(struct sockaddr*)&client_addr ,&client_addr_len);
        if(client_fd < 0 ){
            perror("accept error");
            break;
        }
        cout<<"New Client fd="<<client_fd<<endl;
        pthread_mutex_lock(&mutex);
        Client_Fd_Set.insert(client_fd);
        pthread_mutex_unlock(&mutex);
    }

    unordered_set<int>::iterator  it;
    for(it = Client_Fd_Set.begin() ; it!=Client_Fd_Set.end();++it)
    {
        close(*it);
    }
    close(server_fd);
    cout<<"main_thread exit"<<endl;
    exit(0);
    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IO多路复用机制——Select 的相关文章

  • 尝试了解使用服务打开对话框

    我已经阅读了有关使用 mvvm 模式打开对话框的讨论 我看过几个使用服务的示例 但我不明白所有部分如何组合在一起 我发布这个问题寻求指导 以了解我应该阅读哪些内容 以更好地理解我所缺少的内容 我将在下面发布我所拥有的内容 它确实有效 但从我
  • 将类对象放置在向量中?

    我注意到我可以将一个类放置在一个向量中 这是我的程序 我收到以下错误 out blackjack exe blackjack obj blackjack obj error LNK2019 unresolved external symbo
  • 按扩展名过滤搜索文件返回太多结果

    我正在开发一个 C 控制台应用程序 它必须管理 Windows 操作系统上的文件 我需要获取具有特定扩展名的文件名 列表 我找到了很多解决方案 最建议的是以下一种 HANDLE hFind WIN32 FIND DATA data hFin
  • 无法注册时间触发的后台任务

    对于 Windows 8 应用程序 在 C Xaml 中 我尝试注册后台任务 很难说 但我想我的后台任务已正确注册 但是当我单击调试位置工具栏上的后台任务名称时 我的应用程序停止工作 没有任何消息 我查看了事件查看器上的日志 得到 具有入口
  • cpp.react库的C++源代码中奇怪的“->* []”表达式

    这是我在文档中找到的 C 片段cpp react 库 https github com schlangster cpp react implicit parallelism auto in D MakeVar 0 auto op1 in g
  • 语音识别编程问题入门

    所以 你们可能都看过 钢铁侠 其中托尼与一个名为贾维斯的人工智能系统进行交互 演示剪辑here http www youtube com watch v Go8zsh1Ev6Y 抱歉 这是广告 我非常熟悉 C C 和 Visual Basi
  • 在 C# 中,如何根据在 gridview 行中单击的按钮引用特定产品记录

    我有一个显示产品网格视图的页面 该表内有一列 其中有一个名为 详细信息 的超链接 我想这样做 以便如果用户单击该特定产品的详细信息单元格 将打开一个新页面 提供有关该产品的更多信息 我不确定如何确定哪个Product记录链接的详细信息以及我
  • 如何在 C# Designer.cs 代码中使用常量字符串?

    如何在 designer cs 文件中引用常量字符串 一个直接的答案是在我的 cs 文件中创建一个私有字符串变量 然后编辑 Designer cs 文件以使用此变量 而不是对字符串进行硬编码 但设计者不喜欢这样抛出错误 我明白为什么这行不通
  • 如何使用 x64 运行 cl?

    我遇到了和这里同样的问题致命错误 C1034 windows h 未设置包含路径 https stackoverflow com questions 931652 fatal error c1034 windows h no include
  • 是否使用 C# 数据集? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我对 C 中的数据集概念有点困惑 编码 ASP NET 站点 但这并不重要 在我的阅读中 我了解到它们 本质上 用作我的应用程序和我的
  • 模板外部链接?谁能解释一下吗?

    模板名称具有链接 3 5 非成员函数模板可以有内部链接 任何其他模板名称应具有外部链接 从具有内部链接的模板生成的实体与在其他翻译单元中生成的所有实体不同 我知道使用关键字的外部链接 extern C EX extern C templat
  • 在 C 中使用枚举而不是 #defines 作为编译时常量是否合理?

    在 C 工作了一段时间后 我将回到 C 开发领域 我已经意识到 在不必要的时候应该避免使用宏 以便让编译器在编译时为您做更多的工作 因此 对于常量值 在 C 中我将使用静态 const 变量或 C 11 枚举类来实现良好的作用域 在 C 中
  • 将二变量 std::function 转换为单变量 std::function

    我有一个函数 它获取两个值 x 和 y 并返回结果 std function lt double double double gt mult double x double y return x y 现在我想得到一个常量 y 的单变量函数
  • 将 Word 转换为 PDF - 禁用“保存”对话框

    我有一个用 C 编写的 Word 到 PDF 转换器 除了一件事之外 它工作得很好 有时 在某些 Word 文件上 后台会出现一条消息保存源文件中的更改 gt 是 否 取消 但我没有对源文件进行任何更改 我只想从 Word 文件创建 PDF
  • 模板类的模板构造函数的 C++ 显式模板特化

    我有一个像这样的课程 template
  • Visual Studio 2015:v120 与 v140?

    仅供参考 Win10 x64 我今天开始尝试 Visual Studio 2015 在弄清楚如何运行 C C 部分后 我尝试加载一个大型个人项目 该项目使用非官方的glsdk http glsdk sourceforge net docs
  • 代码中的.net Access Forms身份验证“超时”值

    我正在向我的应用程序添加注销过期警报 并希望从我的代码访问我的 web config 表单身份验证 超时 值 我有什么办法可以做到这一点吗 我认为您可以从 FormsAuthentication 静态类方法中读取它 这比直接读取 web c
  • 了解 Lambda 表达式和委托 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我已经尝试解决这个问题很长一段时间了 阅读在线博客和文章 但到目前为止还没有成功 什么是代表 什么是 Lambda 表达式 两者的优点
  • Java时区混乱

    我正在运行 Tomcat 应用程序 并且需要显示一些时间值 不幸的是 时间快到了 还有一个小时的休息时间 我调查了一下 发现我的默认时区被设置为 sun util calendar ZoneInfo id GMT 08 00 offset
  • 如何在 C 中将 char 连接到 char* ?

    我怎样才能前置char c to char myChar 我有c值为 A and myChar值为 LL 我怎样才能前置c to myChar使 ALL 这应该有效 include

随机推荐

  • 计算机在微表情的应用,一种有效的微表情自动识别方法

    一种有效的微表情自动识别方法 专利摘要 本发明公开了一种有效的微表情自动识别方法 包括 微表情帧序列预处理 微表情信息数据学习和微表情识别 微表情帧序列预处理的方法为 检测获取的微表情序列的帧数 提取每帧图像的数据进行灰度化处理 采用线性插
  • Pytorch DDP原理及配置[最大限度提高GPU利用率]

    前言 pytorch在单机多卡 多机多卡之间已经做了进一步的优化 最早之前可以使用 net torch nn DataParallel net cuda device ids range torch cuda device count 来使
  • 【Transformer系列(3)】 《Attention Is All You Need》论文超详细解读(翻译+精读)

    前言 哒哒 时隔好久终于继续出论文带读了 这次回归当然要出一手王炸呀 没错 今天我们要一起学习的就是传说中的Transformer 在2021年Transformer一经论文 Attention is All You Need 提出 就如龙
  • Python内置对象之字典、集合和序列

    字典 可变类型与不可变类型 字典是 Python 唯一的一个 映射类型 字符串 元组 列表属于序列类型 字典以 关键字 为索引 关键字可以是任意不可变类型 通常用字符串或数值 那么如何快速判断一个数据类型 X 是不是可变类型的呢 两种方法
  • Qt实现串口调试工具

    一 效果图 二 重要方法和函数 模块和头文件 Qt中要调试串口 需要在 pro文件中增加串口调试的模块 代码如下 QT serialport 然后在头文件中 需要包含串口调试的头文件 代码如下所示 include
  • JavaScript 学习笔记之概述

  • Numpy 中clip函数的使用

    Numpy 中clip函数的使用 numpy clip a a min a max out None source 其中a是一个数组 后面两个参数分别表示最小和最大值 怎么用呢 老规矩 我们看代码 import numpy as np x
  • 掌优刷脸支付已经具备商业化能力

    人脸识别支付是一款基于面部识别系统的支付应用 支付时消费者只需要面对自助终端屏幕上的摄像头 系统会获取用户面部信息并将面部信息与支付宝账户关联 通过支付宝账户进行费用支付 整个交易过程十分便捷 首次刷脸支付 在 刷脸页面 进行面部识别 输入
  • (一)Word中如何将表格断开,并且增加空的一行

    在使用word中表格时 有时候希望从当中断开 并且在中间添加一行空白 方便添加文字 如下图所示 将一个4行的表格 弄成2个2行的表格 并且当中要多出一行 那么快捷键如下 选中要断开的那一行 ctr shift enter即可断开
  • bootStrap-table实战详解与问题总结

    背景介绍 说实话 前端一直是我的薄弱项 每次新需求的最大难点就是前端技术的攻克 不仅仅是前端技术的框架繁多 菜也是原罪 这次的需求是在后台调用另外一个系统接口 将返回的数据通过表格的形式展示在页面上 要做一个表格 那选择可真的就太多了 前端
  • Unity与EasyAR

    Unity与EasyAR 从老师哪里接到了一个小项目 具体就是主一款具有AR功能的旅游应用 而AR方面的需求十分简单 就是识别图片 并显示出对应模型就行了 就跟EasyAR的Unity样例一个样子 所以这里就选择使用android跟unit
  • 关于Descriptors cannot not be created directly报错

    报错信息为 TypeError Descriptors cannot not be created directly If this call came from a pb2 py file your generated code is o
  • 在angular中使用ng-zorro-antd组件

    前言 网上关于angular引用ng zorro antd的文章太少了 而且还是7版本的angular 安装的过程踩了不少的坑 特此记录 1 安装ng zorro antd angular的初始化我就不赘述了 安装ng zorro antd
  • 【Linux】makefile学习笔记(网址)

    跟我一起写Makefile https seisman github io how to write makefile functions html 跟我一起写 Makefile 一 陈皓 https blog csdn net haoel
  • 【OpenAI】ChatGPT函数调用(Function Calling)实践

    6月13日OpenAI在Chat Completions API中添加了新的函数调用 Function Calling 能力 帮助开发者通过API方式实现类似于ChatGPT插件的数据交互能力 本文在作者上一篇文章 私有框架代码生成实践 的
  • VC++ 制作滤镜效果(底片效果、雕刻效果、黑白效果)

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 需要源码的请单独与我联系 滤镜是一种改变图像相貌的程序 其本身并不属于图像处理研究的范畴 滤镜程序的核心算法源自数字图像处理
  • jdk版本切换工具jenv使用指南

    1 下载jenv包 下载链接 GitHub FelixSelter JEnv for Windows Change your current Java version with one line 下载JEnv zip 将JENV添加到环境变
  • Java当中判断学生成绩等级的方法

    前言 使用两种方式来判断学生成绩的等级 第一种 if else if else来判断 第二种 使用switch语句来判断 一 if else if else来判断 代码如下 示例 import java util Scanner 题目 使用
  • 前端视频插件Video.js的基本使用

    1 使用前准备 先移步官网对插件进行大致的了解 以判断是否满足需求的需要 官网中的demo相当实用 在下就是官网 2 正式开始 首先引入相关的文件 3 HTML代码部分 div class m div
  • IO多路复用机制——Select

    IO多路复用机制 Select 服务器端使用select机制监听可读的文件描述符 客户端 的一般流程如下 下面将说下 如何将select来实现多并发的双向通信 select函数族如下 服务器端使用select机制监听可读的文件描述符 客户端