Linux网络编程---多路I/O转接服务器(一)

多路I/O转接服务器

多路IO转接服务器也叫做多任务IO服务器。该类服务器实现的主旨思想是,不再由应用程序自己监视客户端连接,取而代之由内核替应用程序监视文件。

主要使用的方法有三种:select、poll、epoll 

一、select多路IO转接

让内核去监听客户端连接(lfd),当有客户端进行连接时 它会让server去调用accetp(当有连接时才去立即调用,而不是一直阻塞等待)得到一个用于通信的cfd,最后让内核监管着lfd和所有cfd

即(原理):借助内核, select 来监听, 客户端连接、数据通信事件。

函数解析

1. 底层原理: 

文件描述符表:前三个默认被系统占用

fd_set集合:传入的是文件描述符,传出所有监听集合(读、写、异常)中满足对应事件的总数

fd_set集合的本质:位图(二进制位存放文件描述符的状态),默认都为0,若发生变化就置1

2. 语法: 

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeva l *timeout);

参数

  • nfds:监听的所有文件描述符中,最大文件描述符+1;
  • readfds:读 文件描述符监听集合。传入传出参数
  • writefds:写 文件描述符监听集合。传入传出参数,通常传NULL
  • exceptfds:异常 文件描述符监听集合。传入传出参数,通常传NULL
  • timeout:大于0表示设置监听时长,NULL表示阻塞监听,0表示非阻塞监听 while轮询

返回值

  • 大于0:所有监听集合(读、写、异常)中满足对应事件的总数
  • 0:没有满足监听条件的文件描述符
  • -1:error

3. 监听集合对应函数:

1.void FD_ZERO(fd_set *set); ---清空一个文件描述符集合             

fd_set rset; FD_ZERO(&rset); //将rset集合清空

2.void FD_SET(int fd, fd_set *set); ---将待监听的文件描述符添加到监听集合中

FD_SET(3,&rset);FD_SET(5,&rset); //将文件描述符3和5加到rset集合中

3.void FD_CLR(int fd, fd_set *set); ---将一个文件描述符从监听集合中移除

4.int FD_ISSET(int fd, fd_set *set); ---判断一个文件描述符是否在该集合中

4. 思路分析: 

代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>#include "wrap.h"#define SERV_PORT 6666int main(int argc, char *argv[])
{int listenfd, connfd;               // connect fdchar buf[BUFSIZ];         /* #define INET_ADDRSTRLEN 16 */struct sockaddr_in clie_addr, serv_addr;socklen_t clie_addr_len;listenfd = Socket(AF_INET, SOCK_STREAM, 0);  int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family= AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port= htons(SERV_PORT);Bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));Listen(listenfd, 128);fd_set rset,allset;//定义读集合,备份集合allsetint ret,maxfd = 0,n;maxfd = listenfd;//最大文件描述符FD_ZERO(&allset);//清空监听集合FD_SET(listenfd,&allset);//将监听fd添加到监听集合中while(1){rset = allset;//备份select(maxfd+1,&rset,NULL,NULL,NULL);//使用select监听if (ret < 0){perr_exit("select error");}if (FD_ISSET(listenfd,&rset))//listenfd满足监听的读事件{clie_addr_len = sizeof(clie_addr);connfd = Accept(listenfd,(struct sockaddr *)&clie_addr,&clie_addr_len);//建立连接---不会阻塞FD_SET(connfd,&allset);//将新产生的fd添加到监听集合中监听数据读事件if (maxfd < connfd)//修改maxfdmaxfd = connfd;if (ret = 1)//说明select只返回一个,并且是listenfd,后续执行无需执行continue;    }for (int i = listenfd+1;i <= maxfd;i++)//处理 满足读事件的fd{if (FD_ISSET(i,&rset))//找到满足读事件的fd{n = read(i,buf,sizeof(buf));if (n == 0)//检测到客户端已经关闭连接{Close(i);FD_CLR(i,&allset);//将关闭的fd移除出监听集合}else if (n == -1){perr_exit("read error");}for (int j = 0;j<n;j++){buf[j] = toupper(buf[j]);}write(i,buf,n);write(STDOUT_FILENO,buf,n);}}}Close(listenfd);return 0;
}

 select优缺点:

优点:跨平台。win、linux、macOS、Unix、类Unix、mips

缺点:监听上限受文件描述符限制。 最大 1024.

           检测满足条件的fd, 自己添加业务逻辑提高小。 提高了编码难度。

*二、poll多路IO转接(半成品):在实际开发过程中用处不大,了解即可,重点是epoll

函数解析:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数:

        fds:监听的文件描述符【数组】

        struct pollfd

        {                
                int fd:待监听的文件描述符     
                short events:待监听的文件描述符对应的监听事件

                            取值:POLLIN、POLLOUT、POLLERR

                short revnets:传入时, 给0。如果满足对应事件的话, 返回 非0 --> POLLIN、POLLOUT、POLLERR
            }

        nfds: 监听数组的,实际有效监听个数。

        timeout:  > 0:  超时时长。单位:毫秒。

                      -1:    阻塞等待

                      0:  不阻塞

返回值:

        返回满足对应监听事件的文件描述符 总个数。

思路分析:

代码实现:

/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include "wrap.h"#define MAXLINE 80
#define SERV_PORT 6666
#define OPEN_MAX 1024int main(int argc, char *argv[])
{int i, j, maxi, listenfd, connfd, sockfd;int nready;ssize_t n;char buf[MAXLINE], str[INET_ADDRSTRLEN];socklen_t clilen;struct pollfd client[OPEN_MAX];struct sockaddr_in cliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));Listen(listenfd, 20);client[0].fd = listenfd;client[0].events = POLLIN; 					/* listenfd监听普通读事件 */for (i = 1; i < OPEN_MAX; i++)client[i].fd = -1; 							/* 用-1初始化client[]里剩下元素 */maxi = 0; 										/* client[]数组有效元素中最大元素下标 */for ( ; ; ) {nready = poll(client, maxi+1, -1); 			/* 阻塞 */if (client[0].revents & POLLIN) { 		/* 有客户端链接请求 */clilen = sizeof(cliaddr);connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));for (i = 1; i < OPEN_MAX; i++) {if (client[i].fd < 0) {client[i].fd = connfd; 	/* 找到client[]中空闲的位置,存放accept返回的connfd */break;}}if (i == OPEN_MAX)perr_exit("too many clients");client[i].events = POLLIN; 		/* 设置刚刚返回的connfd,监控读事件 */if (i > maxi)maxi = i; 						/* 更新client[]中最大元素下标 */if (--nready <= 0)continue; 						/* 没有更多就绪事件时,继续回到poll阻塞 */}for (i = 1; i <= maxi; i++) { 			/* 检测client[] */if ((sockfd = client[i].fd) < 0)continue;if (client[i].revents & POLLIN) {if ((n = Read(sockfd, buf, MAXLINE)) < 0) {if (errno == ECONNRESET) { /* 当收到 RST标志时 *//* connection reset by client */printf("client[%d] aborted connection\n", i);Close(sockfd);client[i].fd = -1;} else {perr_exit("read error");}} else if (n == 0) {/* connection closed by client */printf("client[%d] closed connection\n", i);Close(sockfd);client[i].fd = -1;} else {for (j = 0; j < n; j++)buf[j] = toupper(buf[j]);Writen(sockfd, buf, n);}if (--nready <= 0)break; 				/* no more readable descriptors */}}}return 0;
}

read 函数返回值:      
    > 0:实际读到的字节数

    =0: socket中,表示对端关闭。close()

    -1: 如果 errno == EINTR   被异常终端。 需要重启。

            如果 errno == EAGIN 或 EWOULDBLOCK 以非阻塞方式读数据,但是没有数据。 需要,再次读。

           如果 errno == ECONNRESET  说明连接被 重置。 需要 close(),移除监听队列。

           错误。 

poll优缺点: 

优点:
        自带数组结构。 可以将 监听事件集合 和 返回事件集合 分离。

        拓展 监听上限。 超出 1024限制。

缺点:
        不能跨平台。 Linux

        无法直接定位满足监听事件的文件描述符, 编码难度较大。

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

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

相关文章

js实现倒计时效果

实现效果 实现代码 const computingTime startTime > {// 目标日期const targetDate new Date(startTime).getTime();// 每秒更新倒计时const timer setInterval(function() {// 当前时间const now new Date().getTime();// 距离目标时间的毫秒数const distance targe…

vue3中使用animate.css

在vue3中使用animate.css 20240428_093614 引入&#xff1a;npm install animate.css --save main.js注册&#xff1a;import ‘animate.css/animate.min.css’ 注意&#xff1a;import ‘animate.css’ 不适合在vue3项目 使用&#xff1a;class“animate__animated 动画名称”…

Matlab实现CNN-BiLSTM模型,对一维时序信号进行分类

1、利用Matlab2021b训练CNN-BiLSTM模型&#xff0c;对采集的一维时序信号进行分类二分类或多分类 2、CNN-BiLSTM时序信号多分类执行结果截图 训练进度&#xff1a; 网络分析&#xff1a; 指标变化趋势&#xff1a; 代码下载方式&#xff08;代码含数据集与模型构建&#xff0…

Vue3 v3.4之前如何实现组件中多个值的双向绑定?

文章目录 基础代码1. watch2. computed&#xff08;推荐&#xff09; 官方给的例子是关于el-input的&#xff0c;如下。但是input不是所有组件标签都有的属性啊&#xff0c;有没有一种通用的办法呢&#xff1f; <script setup> defineProps({firstName: String,lastName…

【调研分析】目标在不同焦距和距离下与画面的比例(2.8-3.6-4.0)

之前在做项目中需要极度优化效果和代码运行速度 为此测试了同一个目标在不同焦距和距离下与画面的比例&#xff0c;从而可以方便在指定大小情况下搜索目标 NOTE: 这是早期滑窗检测做目标检测下的工作

浅谈OpenCV 粗略计算工件轮廓面积和外接圆直径(Emgu.CV)

前言 最近领导在做库房工具管理这块的功能&#xff0c;希望能集成OpenCV 粗略的计算出工具的长度&#xff0c;以方便用户再归还工具的时候&#xff0c;提示用户该放在那种尺寸的盒子里面&#xff0c;这便是这篇文章的由来。 我们的系统是基于.net开发的&#xff0c;所以采用的是…

分布式系统事务一致性解决方案(基于事务消息)

参考&#xff1a;https://rocketmq.apache.org/zh/docs/featureBehavior/04transactionmessage/ 文章目录 概要错误的方案方案一&#xff1a;业务方自己实现方案二&#xff1a;RocketMQ 事务消息什么是事务消息事务消息处理流程事务消息生命周期使用限制使用示例使用建议 概要 …

MATLAB语音信号分析与合成——MATLAB语音信号分析学习资料汇总(图书、代码和视频)

教科书&#xff1a;MATLAB语音信号分析与合成&#xff08;第2版&#xff09; 链接&#xff08;含配套源代码&#xff09;&#xff1a;https://pan.baidu.com/s/1pXMPD_9TRpJmubPGaRKANw?pwd32rf 提取码&#xff1a;32rf 基础入门视频&#xff1a; 视频链接&#xff1a; 清…

急急急!微信朋友圈删除了怎么恢复?

微信朋友圈是我们与朋友分享生活点滴的重要平台&#xff0c;但有时候微信出现异常&#xff0c;导致我们编辑好的朋友圈被删除了&#xff0c;这时候该怎么办呢&#xff1f; 幸运的是&#xff0c;微信提供了一种简单的方式来恢复已删除的朋友圈内容。微信朋友圈删除了怎么恢复&a…

利用二叉检索树将文章中的单词建立索引(正则表达式)

知识储备 链接: 【二叉检索树的实现——增删改查、读取命令文件、将结果写入新文件】 1、正则表达式的处理 &#xff08;1&#xff09;r’前缀的作用 r’前缀的用于定义原始字符串&#xff0c;特点是不会处理反斜杠\作为转义字符 &#xff08;2&#xff09;正则表达式中元…

场外个股期权开户新规及操作方法

场外个股期权开户新规 场外个股期权开户新规主要涉及对投资者资产实力、专业知识、风险承受能力和诚信记录的要求。以下是根据最新规定总结的关键要点&#xff1a; 来源/&#xff1a;股指研究院 资产门槛&#xff1a;投资者需具备一定的资产实力&#xff0c;确保在申请开户前…

【Linux】文件打包解压_tar_zip

文章目录 &#x1f4d1;引言&#xff1a;一、文件打包压缩1.1 什么是文件打包压缩&#xff1f;1.2 为什么需要文件打包压缩&#xff1f; 二、打包解压2.1 zip2.2 unzip2.3 tar指令 &#x1f324;️全篇小结&#xff1a; &#x1f4d1;引言&#xff1a; 在Linux操作系统中&#…

OpenCV-Python: 强大的计算机视觉库

文章目录 OpenCV-Python: 强大的计算机视觉库背景OpenCV-Python是什么&#xff1f;安装简单的库函数使用方法场景示例人脸检测和识别图像分割目标跟踪 常见问题和解决方案总结 OpenCV-Python: 强大的计算机视觉库 背景 OpenCV (Open Source Computer Vision Library) 是一个开…

如何修改php版本

我使用的Hostease的Windows虚拟主机产品,由于网站程序需要支持高版本的PHP,程序已经上传到主机&#xff0c;但是没有找到切换PHP以及查看PHP有哪些版本的位置&#xff0c;因此咨询了Hostease的技术支持&#xff0c;寻求帮助了解到可以实现在Plesk面板上找到此切换PHP版本的按钮…

基于Springboot+Vue的Java项目-火车票订票系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

GDPU 算法分析与设计 天码行空5

一、【实验目的】 &#xff08;1&#xff09;熟悉动态规划算法的基本思想. &#xff08;2&#xff09;理解动态规划算法中子问题的划分和递推方程设计的基本方法. &#xff08;3&#xff09;熟悉矩阵链乘法的基本思想并编程实现。 二、【实验内容】 输入:矩阵链Ai…j的输入为…

Power BI:如何将文件夹批量Excel(多sheet页)文件导入?

故事背景&#xff1a; 业务同事想用Power BI分析近两年市场费用。 数据源全部是Excel文件&#xff0c;并且以每月一个Excel文件的方式&#xff0c;统一存放到同一文件夹下面。 重点&#xff0c;每张Excel文件会有多张sheet页&#xff0c;用区分每家分公司的费用信息。 目前…

Linux之进程间通信(二)

system V system V共享内存是内核中专门设计的通信的方式, 粗粒度划分操作系统分为进程管理, 内存管理, 文件系统, 驱动管理.., 粒度更细地分还有 进程间通信模块. 对于操作系统, 通信的场景有很多, 有以传送数据, 快速传送数据, 传送特定数据块, 进程间协同与控制以目的, 它…

数字信号的产生与检测——DSP学习笔记六

本专栏的博客的图片大部分来源于老师的PPT&#xff0c;本博客只是博主对于上课内容的知识结构的分析和梳理。 几种数字信号的产生 正弦波信号 多项式逼近(除了泰勒展开&#xff0c;还有一种方法是切比雪夫逼近法&#xff0c;感兴趣可以自己去了解一下&#xff09; 查找表 核心思…

<计算机网络自顶向下> Internet Protocol

互联网中的网络层 IP数据报格式 ver: 四个比特的版本号&#xff08;IPV4 0100, IPV6 0110&#xff09; headlen&#xff1a;head的长度&#xff08;头部长度字段&#xff08;IHL&#xff09;指定了头部的长度&#xff0c;以32位字&#xff08;4字节&#xff09;为单位计算。这…