Linux编程基础 8.3:I/O多路转接服务器

1 简介

为进一步提升服务器效率,人们提出了一种被称为I/O多路转接的模型。其中“多路”指代连接到服务器的多个客户端程序,而“转接”则是指在服务器主线与各分支之间设置一个“岗位”,由该岗位实现监控多路连接中数据状态的功能,若某路连接中数据就绪,就通知服务器,使主程序对该路请求作出处理。
与多进程和多线程并发服务器相比,I/O多路转接服务器实现了I/O多路复用,系统不必创建多进程或多线程,也不必维护多个进程或线程,因此大大降低了系统开销。
Linux系统实现I/O多路转接函数有:

  • select()
  • poll()
  • epoll()

2 select函数

在这里插入图片描述
图 select通信模型示意图

  • 使用select搭建的I/O多路转接服务器是一种基于非阻塞的服务器;
  • 当有客户端连接请求到达时,accept会返回一个文件描述符,该文件描述符会被存储到由select监控的文件描述符表中,每个文件描述符对应的文件都可进行I/O操作,因此select可通过监控表中各个文件描述符,来获取对应的客户端I/O状态。
  • 若每路程序中都没有数据到达,线程将阻塞在select上;否则select将已就绪客户端程序的数量返回到服务器。
#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
struct timeval{long tv_sec;long tv_user;
};

功能: 监视客户端I/O接口的状态
参数说明:

  • nfds:设置select监控的文件描述符的范围,需设置为文件描述符最大值加1;
  • readfds:可读取数据的文件描述符集,fd_set实质为长整型,这些集合中的每一位都对应一个文件描述符的状态;若集合参数被设置为NULL,表示不关心文件的对应状态;
  • writefds:可写入数据的文件描述符集;
  • exceptfds:发生异常的文件描述符集;
  • timeout:设置select的阻塞时长,其取值有如下几种情况:
    – 若timeval=NULL,表示永远等待;
    – 若timeval>0,表示等待固定时长;
    – 若timeval=0,select将在检查过指定文件描述符后立即返回(轮询)。

Linux系统提供了一系列操作文件描述符集的函数:

函数声明函数功能
void FD_CLR(int fd,fd_set *set);将集合中的文件描述符fd清除(将fd位置为0)
int FD_ISSET(int fd,fd_set *set);测试集合中文件描述符fd是否存在于集合中,若存在则返回非0
void FD_SET(int fd,fd_set *set);将文件描述符fd添加到集合中(将fd位置为1)
void FD_ZERO(fd_set *set);清除集合中所有的文件描述符(所有位置0)

返回值说明:

  • 大于0:表示已就绪文件描述符的数量,此种情况下某些文件可读写或有错误信息;
  • 等于0:表示等待超时,没有可读写或错误的文件;
  • -1:表示出错返回,同时errno将被设置。

特别说明:

  • select可监控的进程数量是有限的,该数量受到两个因素的限制。
    –第一个因素是进程可打开的文件数量;
    –第二个因素是select中的集合fd_set的容量。
  • 进程可打开文件的上限可通过ulimit –n命令或setrlimit函数设置,但系统所能打开的最大文件数也是有限的;
  • select中集合fd_set的容量由宏FD_SETSIZE(定义在linux/posix_types.h中)指定;
  • 即便通过重新编译内核的方式修改FD_SETSIZE,也不一定能提升select服务器的性能,因为若select一次监测的进程过多,单轮询便要耗费大量的时间。

【案例 1】使用select模型搭建多路I/O转接服务器。

  • 服务器:接收客户端数据,并将接收到的数据转为大写,写回客户端;
  • 客户端:向服务器发送数据,并将服务器返回的数据打印到终端。
selectClient.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80					//缓冲数组大小
#define SERV_PORT 8000				//端口号
int main() {struct sockaddr_in tempServAddr;char tempBuf[MAXLINE];int tempSockFd, tempDataLen;tempSockFd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&tempServAddr, sizeof(tempServAddr));tempServAddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &tempServAddr.sin_addr);tempServAddr.sin_port = htons(SERV_PORT);Connect(tempSockFd, (struct sockaddr *)&tempServAddr, sizeof(tempServAddr));while (fgets(tempBuf, MAXLINE, stdin) != NULL) {Write(tempSockFd, tempBuf, strlen(tempBuf));tempDataLen = Read(tempSockFd, tempBuf, MAXLINE);if (tempDataLen == 0){printf("the other side has been closed.\n");} else {Write(STDOUT_FILENO, buf, n);}//of if}//of whileClose(sockfd);return 0;
}//of mainselectServer.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "wrap.h"
#define MAXLINE 80						//缓冲数组大小
#define SERV_PORT 8000					//端口号
int main() {int i, tempMaxI, tempMaxFd, tempListenFd, tempConnFd, tempSockFd;int tempNReady, tempClient[FD_SETSIZE]; 	//FD_SETSIZE 默认为1024ssize_t tempDataLen;fd_set tempRset, tempAllset;char tempBuf[MAXLINE];char tempStr[INET_ADDRSTRLEN];			//#define INET_ADDRSTRLEN 16socklen_t tempCliAddrLen;struct sockaddr_in tempCliAddr, tempServAddr;tempListenFd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&tempServAddr, sizeof(tempServAddr));tempServAddr.sin_family = AF_INET;tempServAddr.sin_addr.s_addr = htonl(INADDR_ANY);tempServAddr.sin_port = htons(SERV_PORT);Bind(tempListenFd, (struct sockaddr *)&tempServAddr, sizeof(tempServAddr));Listen(tempListenFd, 20);		//默认最大128tempMaxFd = tempListenFd;tempMaxI = -1;//初始化监控列表for (i = 0; i < FD_SETSIZE; i++){tempClient[i] = -1; 	//使用-1初始化client[]中元素}//of for iFD_ZERO(&tempAllset);FD_SET(tempListenFd, &tempAllset); 	//将listenfd添加到文件描述符集中//循环监测处于连接状态进程的文件描述符for (;;) {//使用变量rset获取文件描述符集合tempRset = tempAllset;//记录就绪进程数量tempNReady = select(tempMaxFd + 1, &tempRset, NULL, NULL, NULL);if (tempNReady < 0){perr_exit("select error");}//of ifif (FD_ISSET(tempListenFd, &tempRset)){//有新连接请求到达则进行连接便处理连接请求tempCliAddrLen = sizeof(tempCliAddr);tempConnFd = Accept(tempListenFd, (struct sockaddr *)&tempCliAddr, &tempCliAddrLen);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &tempCliAddr.sin_addr, tempStr, sizeof(tempStr)), ntohs(tempCliAddr.sin_port));for (i = 0; i < FD_SETSIZE; i++){if (tempClient[i] < 0) {tempClient[i] = tempConnFd ; 	//将文件描述符connfd保存到client[]中break;}//of if}//of for iif (i == FD_SETSIZE) { 		//判断连接数是否已达上限fputs("too many clients\n", stderr);exit(1);}//of ifFD_SET(tempConnFd, &tempAllset); 	//添加新文件描述符到监控信号集中if (tempConnFd > tempMaxFd) {		//更新最大文件描述符tempMaxFd = tempConnFd;}//of ifif (i > tempMaxI) {				//更新client[]最大下标值tempMaxI = i;}//of if//若无文件描述符就绪,便返回select,继续阻塞监测剩余的文件描述符if (--tempNReady == 0){continue;}//of if}//of if//遍历文件描述符集,处理已就绪的文件描述符for (i = 0; i <= tempMaxI; i++) {if ((tempSockFd = tempClient[i]) < 0){continue;}//of ifif (FD_ISSET(tempSockFd, &tempRset)) {//n=0,client就绪但未读到数据,表示client将关闭连接if ((tempDataLen = Read(tempSockFd, tempBuf, MAXLINE)) == 0) {//关闭服务器端连接Close(tempSockFd);FD_CLR(tempSockFd, &tempAllset); //清除集合中对应的文件描述符tempClient[i] = -1;} else {//处理获取的数据int j;for (j = 0; j < tempDataLen; j++){tempBuf[j] = toupper(tempBuf[j]);}//of for jWrite(sockfd, buf, tempDataLen);}//of ifif (--tempNReady == 0){break;}//of if}//of if}//of for i}//of forClose(tempListenFd);return 0;
}//of main

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

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

相关文章

handler原子锁_多线程编程之原子锁

在《多线程编程之数据访问互斥》一文中简单介绍了原子锁&#xff0c;这里再详细说一下原子锁的概念和用途。(1)简单数据操作如果在一个多线程环境下对某个变量进行简单数学运算或者逻辑运算&#xff0c;那么就应该使用原子锁操作。因为&#xff0c;使用临界区、互斥量等线程互斥…

python统计中文字符的个数_python统计中文字符数量的两种方法

方法一&#xff1a; def str_count(str): 找出字符串中的中英文、空格、数字、标点符号个数 count_en count_dg count_sp count_zh count_pu 0 for s in str: # 英文 if s in string.ascii_letters: count_en 1 # 数字 elif s.isdigit(): count_dg 1 # 空格 elif s.issp…

测井储层参数预测+人工智能方法

1 问题描述 测井储层参数预测 地层泥质含量&#xff1b;地层孔隙度&#xff08;POR&#xff09;&#xff1b;含水饱和度&#xff08;SW&#xff09;&#xff1b;渗透率&#xff08;PERM&#xff09;。 输入&#xff1a;声波时差&#xff08;AC&#xff09;、补偿中子&#x…

php spry文本域_Spry框架及验证构件

图9-71Spry框架原理虽然这些文件存放在服务器上&#xff0c;但实际上是被加载到浏览器中运行的。框架的用户将所有需要的文件链接到HTML文档中&#xff0c;以使用不同的组件&#xff0c;来获得更丰富的用户体验。Spry框架是和设计人员紧密相关的&#xff0c;因此框架中的每个元…

网络系统安全课程--目录

本系列教程以《网络与系统安全》为基础&#xff0c;希望在大家的帮助下&#xff0c;逐步完善网络与系统安全研究生课程。 1 经典攻击分析 1.1 中间人攻击 1.2 源替换攻击 1.3 消息重放攻击 1.4 平行会话攻击 1.5 反射攻击 1.6 交错攻击 1.7 类型缺陷攻击 1.8 身份标识省略导致…

windows 安装openssh服务端_Git神器| SourceTree安装使用教程

SourceTree 是 Windows 和Mac OS X 下免费的Git客户端管理工具。支持创建、克隆、提交、push、pull 和合并等操作。一、sourcetree的安装1. 下载sourcetree下载链接&#xff1a;Sourcetree | Free Git GUI for Mac and Windows2. 安装sourcetree点击安装&#xff0c;第一个创建…

c语言小学生四则运算出题_软件工程第一次作业,小学生四则运算的出题程序...

一、背景阿超有个儿子上小学二年级&#xff0c;老师每天让家长给孩子出30道加减法题&#xff0c;虽然不多&#xff0c;但是每天都做也算是个负担&#xff0c;阿超作为一个老牌程序员当然想用计算机来解决这个小问题&#xff0c;目前对于这个问题对于任何语言都不是问题&#xf…

中国大学生计算机设计大赛--软件应用与开发类--经验总结

1 大赛介绍 中国大学生计算机设计大赛是我国高校面向本科生最早的赛事之一&#xff0c;自2008年开赛至2019年&#xff0c;一直由教育部高校与计算机相关教指委等或独立或联合主办。此赛目前是全国普通高校大学生竞赛排行榜榜单赛事之一。 我们参加了很多届大赛&#xff0c;在2…

python如何使用ppip安装xlwt_Python-xlwt库的基本使用

安装xlwt库 pip install xlwt 基本使用 ①创建工作簿 wa xlwt.Workbook() ②添加工作表 添加“class”工作表 b wa.add_sheet(class) ③向工作表写入数据 注释&#xff1a;(0,0,姓名)(行、列、内容)第0行的第0个也就是excel中的A1&#xff0c;(0,1)就是B1..... b.write(0,0,姓…

网络与系统安全1.1 中间人攻击

1 Diffie-Hellman密钥交换过程 2 中间人攻击过程

tomcat启动占了12g_tomcat服务为何报内存相关错误??

本帖最后由 linux_love 于 2014-9-19 11:46 编辑多谢各位英雄支持&#xff0c;这个问题困扰我N久了&#xff0c;昨天终于让我给拿下了&#xff0c;在Linux下有个CommitLimit 用于限制系统应用使用的内存资源&#xff0c;#grep -i commit /proc/meminfoCommitLimit: 20389524…

python调用键盘_使用 Python 控制自己的电脑和键盘是一种什么样的体验?

可能有时候你需要在电脑做一些重复的点击或者提交表单等操作&#xff0c;如果能通过 Python 预先写好相关的操作指令&#xff0c;让它帮你操作&#xff0c;然后你自己爱干嘛干嘛去&#xff0c;有点 “按键精灵” 的意思&#xff0c;是不是感觉有点爽呢&#xff1f; 那么如何使用…

网络与系统安全4.1 基于随机搜索的黑盒对抗攻击方法

1 摘要 神经网络模型容易受到对抗样本的攻击&#xff0c;根据攻击者是否能获取到模型内部参数可以把攻击类型分为白盒攻击和黑盒攻击。 白盒攻击比较常见&#xff0c;但其对目标模型的威胁程度要远低于黑盒攻击。传统黑盒攻击往往都是基于梯度实现的&#xff0c;并且具有查询…

mybatis 二级缓存失效_二级缓存updateBatchById失效

### 当前使用版本(必须填写清楚&#xff0c;否则不予处理)3.3.0### 该问题是怎么引起的&#xff1f;**([最新版](https://search.maven.org/search?qg:com.baomidou%20a:mybatis-*)上已修复的会直接**close**掉)**### 重现步骤**字典mapper**CacheNamespacepublic interface D…

centos 安装jdk_centos7配置jdk

一、查看centos7是否有自带jdk查看是否安装过java rpm -qa | grep java 如果是centos 一般会自带两个openjdk rpm -e --nodeps 要卸载的包 (包通过上面的指令可以获取到)] 将显示java的包全都卸载 命令&#xff1a; rpm -e --nodeps 要卸载的包二、创建/opt/app目录,用于安装JD…

隐私计算 1 隐私计算的定义与背景

1 隐私计算的定义 隐私计算&#xff1a;在保证数据提供方不泄露原始数据的前提下&#xff0c;对数据进行分析计算的一系列技术&#xff0c;保障数据在流通和融合过程中“可用不可见”。 隐私计算涉及到的学科&#xff1a; 密码学&#xff1b;统计学&#xff1b;计算机体系结…

php object 对象不存在。增加对象_PHP核心

1、单例模式单例模式三要素&#xff1a;拥有一个构造函数&#xff0c;并且为private拥有一个静态成员变量来保持类的实例拥有一个访问这个实例的静态方法<?php //Instan.php 单例类class Instan{static public $instance null;private function __construct(){var_dump(…

数据库查询求小于_SQL学习笔记(二)简单查询

本篇主要学习如何通过简单查询获取想要的数据。从零学会SQL&#xff1a;简单查询​www.zhihu.com基本的查询语句从数据库中查找数据时要使用select子句&#xff0c;select子句是sql中使用最多、最基本的子句。子句是sql语句的组成要素&#xff0c;是以某一个关键字作为开始的语…

隐私计算 2.1秘密共享--问题模型及定义

1 秘密共享问题模型 1.1 富翁和三个儿子的故事 问题&#xff1a; 富翁想在自己的三个儿子中找一个最聪明的来继承自己的遗产&#xff0c;在保护好财富的同时&#xff0c;让其他两个人也参与进来&#xff0c;使得兄弟间和睦相处&#xff0c;家庭和谐。 大儿子的方案&#xff…

为什么手机上传图片这么慢 前端_为什么手机上传速度那么慢?

展开全部1、是由于手机内部存储器写入e69da5e6ba9062616964757a686964616f31333365633861速度慢导致的。可以更换速度更快的内部存储器来解决。2、如果是将手机直接用数据线连到电脑&#xff0c;一般传送文件很慢&#xff0c;这是因为手机的通讯端口波特率限制的&#xff0c;虽…