网络编程(8.15)io模型,IO多路复用(select,poll)

news2024/12/22 17:21:29

1.使用select函数实现IO多路复用

使用select函数实现IO多路复用的服务器:

#include<stdio.h>
#include<head.h>
#include<netinet/in.h>
#include<sys/select.h>
#include<arpa/inet.h>
#define PROT 1112
#define IP "192.168.125.232"
int main(int argc, const char *argv[])
{
    //创建流式套接字
    int sfd=socket(AF_INET,SOCK_STREAM,0);
    if(sfd<0)
    {   
        ERR_MSG("socket");
        return -1; 
    }   
    printf("sfd=%d\n",sfd);
    //设置允许端口被快速复用
    int reuse=1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
    {   
        ERR_MSG("setsockopt");
        return -1; 
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
    printf("允许端口快速复用成功\n");
    //绑定服务器的IP和端口--->必须绑定
    struct sockaddr_in sin;
    sin.sin_family      = AF_INET;//必须填AF_INET
    sin.sin_port        = htons(PROT);//端口号:1024~49151
    sin.sin_addr.s_addr = inet_addr(IP);//本机IP
    if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
    {   
        ERR_MSG("bind");
        return -1; 
    }   
    printf("绑定成功\n");

    //将套接字设置为被动监听状态
    if(listen(sfd,128)<0)
    {   
        ERR_MSG("listen");
        return -1; 
    }   
    printf("被动监听状态设置成功\n");
    //创建一个读集合
    fd_set readfds,tempfds;
    //fd_set本质是一个结构体,成员是一个long类型的数组
    //集合目的是为了存储让内核监测的文件描述符,不初始化有可能随机到有效的但不需要监测的文件描述符
    //需要将集合初始化为无意义的文件描述符
    FD_ZERO(&readfds);
    FD_ZERO(&tempfds);

    //将需要的文件描述符添加到读集合中
    FD_SET(0,&readfds);
    FD_SET(sfd,&readfds);
    int s_res=-1;
    int nfd=-1;
    int maxfd=sfd;
    char buf[128]="";
    ssize_t res=0;
    struct sockaddr_in cin;//存储客户端地址信息
    socklen_t addrlen = sizeof(cin);
    struct sockaddr_in saveCin[1024];
    while(1)
    {   
        tempfds = readfds;
        //调用io多路复用函数 select()
        s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
        if(s_res < 0)
        {
            ERR_MSG("select");
            return -1; 
        }
        else if(0 == s_res)
        {
            printf("timeout...\n");
            break;
        }
        printf("__%d__\n",__LINE__);
        //能运行到当前位置,代表集合中有文件描述符准备就绪
        //此时集合中路会只剩下触发事件的文件描述符
        //例如:
        //当0号文件描述符触发事件,则集合中只剩下0
        //当sfd文件描述符触发事件,则集合中只剩下sfd
        //当0和sfd同时触发,集合中剩下0和sfd
        //判断哪个文件描述符准备就绪,走对应处理函数
        //判断集合中剩下哪个文件描述符,代表哪个文件描述符准备就绪
        //判断nfd是否在集合中,若不在集合中则走recv函数
        for(int i=0;i<=maxfd;i++)
        {
            if(FD_ISSET(i,&tempfds)==0)//判断i是否在集合中,不在进入下一个循环
                continue;
            if(0 == i)
            {
                printf("触发键盘输入事件\n");
                fgets(buf,sizeof(buf),stdin);
                buf[strlen(buf)-1]=0;
                printf(": %s\n",buf);
            }
            else if(sfd == i)
            {
                printf("触发客户端连接事件\n");
                nfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
                if(nfd<0)
                {
                    ERR_MSG("accept");
                    return -1; 
                }
                printf("[%s:%d]nfd=%d,客户端连接成功\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),nfd);
                saveCin[nfd]=cin;
                //将nfd添加到集合中
                FD_SET(nfd,&readfds);
                //更新maxfd
                maxfd = maxfd>nfd ? maxfd : nfd;    
            }
            else
            {
                printf("客户端交互\n");
                //接收数据
                bzero(buf,sizeof(buf));
                res=recv(i,buf,sizeof(buf),0);
                if(res<0)
                {
                    ERR_MSG("recv");
                    return -1; 
                }
                else if(0==res)
                {
                    printf("[%s:%d]nfd=%d 客户端下线\n",inet_ntoa(saveCin[i].sin_addr),ntohs(saveCin[i].sin_port),i);
                    //关闭下线客户端的文件描述符
                    close(i);
                    //将下线客户端的文件描述符从集合中删除
                    FD_CLR(i,&readfds);
                    //更新maxfd
                    //从目前记录的最大fd开始判断是否在集合中,存在为最大,
                    //不存在往前开始判断前一个在不在,直到判断到在的为最大
                    while(FD_ISSET(maxfd,&readfds)==0&&maxfd-->=0);
                    /* 
                       int j=maxfd; 
                       for(;j>=0;j--)
                       {
                       if(FD_ISSET(maxfd,&readfds))
                       break;
                       maxfd=j;
                       }*/
                    continue;
                }
                printf("nfd=%d : %s\n",i,buf);
                //发送数据
                strcat(buf,"*_*");
                if(send(nfd,buf,sizeof(buf),0)<0)
                {
                    ERR_MSG("send");
                    return -1; 
                }
                printf("发送成功\n");
            }
        }
    }   
    //关闭所有文件描述符
    close(sfd);
    return 0;
}                

使用select函数实现IO多路复用的客户端:

#include<stdio.h>
#include<head.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>
#define PROT 1112//服务器绑定的端口
#define IP "192.168.125.232"//服务器绑定的IP端口
int main(int argc, const char *argv[])
{
    //创建流式套接字
    int cfd=socket(AF_INET,SOCK_STREAM,0);
    if(cfd<0)
    {   
        ERR_MSG("socket");
        return -1; 
    }   
    printf("cfd=%d\n",cfd);
    //绑定客户端的IP和端口--->非必须绑定
    //连接服务器
    //先填充服务器的地址信息结构体,真实地址信息结构体根据地址族制定
    struct sockaddr_in sin;
    sin.sin_family   = AF_INET;
    sin.sin_port     = htons(PROT);//服务器绑定的端口号
    sin.sin_addr.s_addr     = inet_addr(IP);//服务器绑定的本机IP
    //连接服务器
    if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0)
    {   
        ERR_MSG("connect");
        return -1; 
    }   
    printf("连接服务器成功\n");
    char buf[128]="";
    ssize_t res=0;
    int s_res=-1;
    //创建读集合
    fd_set readfds,tempfds;
    FD_ZERO(&readfds);
    FD_ZERO(&tempfds);
    FD_SET(0,&readfds);
    FD_SET(cfd,&readfds);
    while(1)
    {   
        tempfds=readfds;
        s_res=select(cfd+1,&tempfds,NULL,NULL,NULL);
        if(s_res<0)
        {
            ERR_MSG("select");
            return -1; 
        }
        else if(0==s_res)
        {
            printf("time out...\n");
            break;
        }
        printf("__%d__\n",__LINE__);
        //能运行到这代表集合中有文件描述符准备就绪
        //判断哪个文件描述符准备就绪,运行对应函数
        if(FD_ISSET(0,&tempfds))
        {
            printf("触发键盘输入事件\n");
            bzero(buf,sizeof(buf));
            fgets(buf,sizeof(buf),stdin);
            buf[strlen(buf)-1]=0;
            //发送数据
            if(send(cfd,buf,sizeof(buf),0)<0)
            {
                ERR_MSG("send");
                return -1; 
            }
            printf("发送成功\n");
        }
        else if(FD_ISSET(cfd,&tempfds))
        {
            printf("触发服务器交互\n");
            bzero(buf,sizeof(buf));
            //接收数据
            res=recv(cfd,buf,sizeof(buf),0);
            if(res<0)
            {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
                ERR_MSG("recv");
                return -1; 
            }
            else if(0==res)
            {
                printf("[%s:%d]cfd=%d 服务器掉线\n",IP,PROT,cfd);
                break;
            }
            printf("[%s:%d]cfd=%d : %s\n",IP,PROT,cfd,buf);
        }
    }   
    //关闭所有文件描述符
    close(cfd);
    return 0;
} 

 使用poll函数实现IO多路复用的客户端:

#include<stdio.h>
#include<head.h>
#include<poll.h>
#define PROT 1112//服务器绑定的端口
#define IP "192.168.125.232"//服务器绑定的IP端口
int main(int argc, const char *argv[])
{
    //创建流式套接字
    int cfd=socket(AF_INET,SOCK_STREAM,0);
    if(cfd<0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("cfd=%d\n",cfd);
    //绑定客户端的IP和端口--->非必须绑定
    //连接服务器
    //先填充服务器的地址信息结构体,真实地址信息结构体根据地址族制定
    struct sockaddr_in sin;
    sin.sin_family   = AF_INET;
    sin.sin_port     = htons(PROT);//服务器绑定的端口号
    sin.sin_addr.s_addr     = inet_addr(IP);//服务器绑定的本机IP
    //连接服务器
    if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0)
    {
        ERR_MSG("connect");
        return -1;
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
    printf("连接服务器成功\n");
    char buf[128]="";
    ssize_t res=0;
    int p_res=-1;
    //定义集合
    struct pollfd fds[2];
    //将需要监测的文件描述符添加到集合
    fds[0].fd=0;//制定监测0号文件描述符
    fds[0].events=POLLIN;
    fds[1].fd=cfd;
    fds[1].events=POLLIN;

    while(1)
    {
        p_res=poll(fds,2,-1);
        if(p_res<0)
        {
            ERR_MSG("poll");
            return -1;
        }
        else if(0==p_res)
        {
            printf("time out...\n");
            break;
        }
        //能运行到这代表集合中有文件描述符准备就绪
        //判断哪个文件描述符准备就绪,运行对应处理函数
        //判断集合中的每个文件描述符中是否有pollin
        //从revents中将代表pollin的那一位单独提取出来,判断是1还是0
        if((fds[0].revents&POLLIN)!=0)
        {
            printf("触发键盘输入事件\n");
            bzero(buf,sizeof(buf));
            fgets(buf,sizeof(buf),stdin);
            buf[strlen(buf)-1]=0;
            //发送数据
            if(send(cfd,buf,sizeof(buf),0)<0)
            {
                ERR_MSG("send");
                return -1;
            }
            printf("发送成功\n");
        }

        if(fds[1].revents&POLLIN)
        {
            printf("触发服务器交互\n");
            bzero(buf,sizeof(buf));
            //接收数据
            res=recv(cfd,buf,sizeof(buf),0);
            if(res<0)
            {
                ERR_MSG("recv");
                return -1;
            }
            else if(0==res)
            {
                printf("[%s:%d]cfd=%d 服务器掉线\n",IP,PROT,cfd);
                break;
            }
            printf("[%s:%d]cfd=%d : %s\n",IP,PROT,cfd,buf);
        }
    }
    //关闭所有文件描述符
    close(cfd);
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

缓存淘汰算法(LFU LRU FIFO)及进程的状态和转换

目录 一、缓存淘汰算法 1.LFU&#xff08;Least Frequently Used&#xff09;最近最不常用算法 2.LRU&#xff08;Least Recently User&#xff09;最近最少使用算法 3.FIFO&#xff08;First in first out&#xff09;先进先出算法 二、进程的状态和转换 1.最基本的三种状…

解决Mac系统android monitor启动时卡住,显示白屏的问题

一.启动环境 清安装1.8版本的jdk&#xff0c;java1.8版本以上不支持android monitor&#xff1b;如果你电脑上安装有java 11等高级别的版本&#xff0c;请自行搜索&#xff0c;如果在mac上安装多jdk&#xff0c;以及如何切换到1.8版本上 二.解决方案 请更新SWT插件&#xff…

x86架构芯片启动过程分析

1、上电启动顺序 上电自检 读取ROM里的bios程序 bios程序会进行硬件检测&#xff0c;比如&#xff1a;内存、硬盘、显卡等 bios完成自检后&#xff0c;需要选择引导设备。比如设备上有U盘、SSD、eMMC、机械硬盘&#xff0c;bios需要知道从哪个启动介质去启动计算机 bios操作界面…

资料分析(四)—— 倍数、比重、平均数

倍数 现期倍数 &#xff08;A是B的几倍&#xff09;&#xff1a; 多几倍 1 增长率 1 增长倍数&#xff08;A比B多几倍&#xff09;&#xff1a; - 1 是几倍 - 1 增长率&#xff08;增长几倍&#xff09; 超过倍数&#xff08;A超过B的 n 倍&#xff09;&#xff1a;A …

数据库--MySQL三大范式、多表查询、函数sql

数据库相关链接&#xff1a; 数据库基础操作--增删改查&#xff1a;http://t.csdn.cn/189CF 数据库--数据类型&#xff1a;http://t.csdn.cn/NnBsY​​​​​​​ 数据库--SQL关键字的执行顺序&#xff1a; http://t.csdn.cn/MoJ4i 一、什么是范式&#xff1f; 范式是数据库…

Android Settings 无障碍设置显示大小页面重复加载问题

基于Android 11&#xff0c;跟踪源码 显示大小页面 packages/apps/Settings/src/com/android/settings/display/PreviewSeekBarPreferenceFragment.java 通过commit() 提交更新页面显示大小。该方法是是在其父类PreviewSeekBarPreferenceFragment 实现调用。 基类预览滑动进度…

Vue-6.创建Vue项目

使用预设默认配置创建Vue项目 创建一个简单的 Vue 项目需要使用 Vue CLI&#xff08;命令行界面&#xff09;。Vue CLI 是一个用于快速构建 Vue.js 项目的工具&#xff0c;它可以帮助你设置项目的基本结构、配置以及开发环境。 以下是创建一个简单的 Vue 项目的步骤&#xff…

机器学习基础之《分类算法(1)—sklearn转换器和估计器》

一、转换器 1、什么是转换器 之前做特征工程的步骤&#xff1a; &#xff08;1&#xff09;第一步就是实例化了一个转换器类&#xff08;Transformer&#xff09; &#xff08;2&#xff09;第二步就是调用fit_transform&#xff0c;进行数据的转换 2、我们把特征工程的接口称…

idea中Maven报错Unable to import maven project: See logs for details问题的解决方法

idea中Maven报错Unable to import maven project: See logs for details问题的解决方法。 在查看maven的环境配置和idea的maven配置后&#xff0c;发现是idea 2020版本和maven 3.9.3版本的兼容性问题。在更改为Idea自带的maven 3.6.1版本后问题解决&#xff0c;能成功下载jar包…

个人对哈希数据结构学习总结 -- 实践篇 -- 上

个人对哈希数据结构学习总结 -- 实践篇 -- 上 引言最佳实践Java篇HashMapgetput扩容 ConcurrentHashMapgetput扩容协作扩容读为什么可以不加锁&#xff1f; ThreadLocalMapgetput扩容delete为什么遍历到null桶就可以判断key不存在&#xff1f;ThreadLocalMap为什么不需要锁&…

【uniapp】picker mode=“region“ 最简单的省市区 三级联动

省市区 picker template <picker mode"region" :value"date" class"u-w-440" change"bindTimeChange"><u--inputborder"bottom"class"u-fb u-f-s-28"placeholder"请选择省市区"type"te…

从零实战SLAM-第八课(非特征点的视觉里程计)

在七月算法报的班&#xff0c;老师讲的蛮好。好记性不如烂笔头&#xff0c;关键内容还是记录一下吧&#xff0c;课程入口&#xff0c;感兴趣的同学可以学习一下。 --------------------------------------------------------------------------------------------------------…

ARM04cortex-A7核LED灯实验

文章目录 一、核心板二、扩展板二、硬件术语2.1 原理图2.2 PCB板2.3 丝印2.4 网络编号 三、分析电路图3.1 思路3.2 总结3.3 工作原理 实验目的&#xff1a;实现LED1/LED2/LED3三盏灯工作 一、核心板 二、扩展板 二、硬件术语 2.1 原理图 原理图是用来描述PCB板子上各个硬件连接…

深入理解epoll

文章目录 概述1. epoll_create - 创建一个epoll实例2. epoll_ctl - 控制epoll实例的事件结构体介绍events取值&#xff1a;data&#xff1a; 联合体&#xff08;共用体&#xff09;&#xff1a; 3. epoll_wait - 等待事件发生伪代码总结 概述 在网络编程中&#xff0c;高效地处…

2023 年牛客多校第九场题解

B Semi-Puzzle: Brain Storm 题意&#xff1a;给定 a , m a,m a,m&#xff0c;构造一个非负整数 u u u&#xff0c;使得 a u ≡ u ( m o d m ) a^u \equiv u \pmod m au≡u(modm)。 1 ≤ a < m ≤ 1 0 9 1 \le a<m \le 10^9 1≤a<m≤109&#xff0c; 0 ≤ u ≤ 1 …

Elasticsearch:如何在 Ubuntu 上安装多个节点的 Elasticsearch 集群 - 8.x

Elasticsearch 是一个强大且可扩展的搜索和分析引擎&#xff0c;可用于索引和搜索大量数据。 Elasticsearch 通常用于集群环境中&#xff0c;以提高性能、提供高可用性并实现数据冗余。 在本文中&#xff0c;我们将讨论如何在 Ubuntu 20.04 上安装和配置具有多节点集群的 Elast…

centos7安装protobuf|序列化和反序列化工具

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总https://blog.csdn.net/yu_cblog/categ…

如何利用视频监控与AI智能识别技术实现铁塔基站机房无人值守?

一、项目背景 很多通信铁塔和机房类项目&#xff0c;都呈现高密度、网格化分布的特点&#xff0c;铁塔基站大多都分布在公路边、高山、野外等区域&#xff0c;巡检难度大&#xff0c;维护效率低&#xff1b;基站设备众多且监控方式单一&#xff0c;而且时刻面临着非法闯入、被…

linux学习(进程替换)[10]

创建子进程 fork()创建子进程进行替换&#xff0c;不影响父进程&#xff0c;父进程聚焦在&#xff1a;读取数据、解析数据、指派进程、执行代码的功能。 子进程发生替换后的数据 在加载新程序进去之前&#xff0c;父子之间的的代码是共享的&#xff0c;数据写时拷贝进子进程…

Webshell实例分析解析

Webshell的实例分析 LD_PRELOAD的劫持在 web 环境中实现基于 LD_PRELOAD 的 RCE 命令执行利用 mail 函数启动新进程 绕过不含字母和数字的Webshell异或取反 LD_PRELOAD的劫持 LD_PRELOAD是Linux/Unix系统的一个环境变量&#xff0c;它影响程序的运行时的链接&#xff08;Runti…