Unix C语言编写基于IO多路复用的小型并发服务器

背景介绍

如果服务器要同时处理网络上的套接字连接请求和本地的标准输入命令请求,那么如果我们使用accept来接受连接请求,则无法处理标准输入请求;类似地,如果在read中等待一个输入请求,则无法处理网络连接的请求。

所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但 select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而还有一种情况是异步IO,异步I /O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

使用select实现IO多路复用

我们可以使用select函数来实现等待一组描述符准备好读。select函数处理类型为fd_set的集合,也叫做描述符集合。逻辑上,我们可以将描述符集合看成一个大小为n的位向量,每个位对应一个描述符。select函数是一个阻塞函数,即只有等到读集合中至少有一个描述符可以读时,就不会阻塞,开始处理请求了。

代码如下:

#include "csapp.h"

//此程序是使用基于 IO多路复用的并发服务器

void echo(int connfd)
{
	int n;
	char buf[MAXLINE];
	rio_t rio;

	rio_readinitb(&rio,connfd);
	//带缓冲的读取函数
	while((n=rio_readlineb(&rio,buf,MAXLINE))>0) {
		//向连接符写入内容
		printf("server received %d bytes \n",n);
		rio_writen(connfd,buf,n);
	}
}

/*command是作为键盘输入时执行的驱动动作*/
void command(void) {
	char buf[MAXLINE];
	printf("you input just now!\n");
	//从标准输入中读取输入到buf中存储
	if(!fgets(buf,MAXLINE,stdin))
		exit(0);
	//输出buf中的数据
	printf("%s",buf);
}

//主程序
int main(int argc,char **argv)
{
	//监听符,连接符,端口号
	int listenfd,connfd,port;
	//套接字地址结构的大小
	socklen_t clientlen=sizeof(struct sockaddr_in);
	//新建套接字地址结构
	struct sockaddr_in clientaddr;
	//fd_set为描述符集合,此处定义了两个read_set,ready_set描述符集合,分别是读集合/准备好集合
	fd_set read_set,ready_set;

	//如果运行时参数小于2,则提示错误
	if(argc!=2) {
		fprintf(stderr,"usage :%s <port>\n",argv[0]);
		exit(0);
	}

	//将第二个参数转化为整型端口号,args to integer
	port=atoi(argv[1]);

	//打开端口号,返回监听描述符
	listenfd=open_listenfd(port);

	//清空读集合
	FD_ZERO(&read_set);

	//将标准输入加到读集合
	FD_SET(STDIN_FILENO,&read_set);

	//将监听描述符加到读集合
	FD_SET(listenfd,&read_set);

	//服务器监听处理主程序
	while(1) {

		//将读集合赋值给准备好集合
		ready_set=read_set;

		//select函数会要求内核挂起进程,等待一个或多个IO事件发生后,才将控制返回给应用程序,就像在下面的示例一样
		select(listenfd+1,&ready_set,NULL,NULL,NULL);

		//有IO事件后,将判断是来自从键盘上键入命令还是从客户端发来的请求,分别给出不同的回应
		if(FD_ISSET(STDIN_FILENO,&ready_set))
			command();
		if(FD_ISSET(listenfd,&ready_set)) {
			connfd=accept(listenfd,(SA *)&clientaddr,&clientlen);
			printf("client connected!");
			//向连接符回送数据
			echo(connfd);
			//关闭连接符,释放资源
			close(connfd);
		}
	}
}

测试部分

[email protected]:~$ telnet localhost 9999
Trying 127.0.0.1...
Connected to localhost.
Escape character is ‘^]‘.
hello
hello
[email protected]:~/doc_main/CProgram/Concurrency$ ./select.o 9999
client connected!server received 7 bytes
时间: 2024-08-25 09:11:50

Unix C语言编写基于IO多路复用的小型并发服务器的相关文章

IO多路复用, 基于IO多路复用+socket实现并发请求(一个线程100个请求), 协程

一. IO多路复用 IO多路复用作用:检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写) 二. 基于IO多路复用+socket实现并发请求(一个线程100个请求) IO多路复用 socket非阻塞 基于事件循环实现的异步非阻塞框架:aaaa.py 非阻塞:不等待 异步:执行完某个任务后自动调用我给他的函数. Python中开源 基于事件循环实现的异步非阻塞框架 Twisted # aaaa.py import socket import select cla

Unix C语言编写基于进程的小型并发服务器

并发介绍 如果逻辑控制流在时间上是重叠的,那么它们就是并发的,可以出现在计算机系统的不同层面上,硬件异常处理程序.进程和Unix信号处理程序都是并发的.并发可以看作是操作系统内核用来运行多个应用程序的机制,但是并发不局限于内核.它也可以在应用程序中扮演角色.并发的主要作用有:访问慢速IO设备;与人交互的程序;通过推迟工作以降低延迟;服务多个网络客户端的请求.并发通常可以有三种,基于进程.基于IO多路复用.基于线程. 基于进程的并发 进程是一个程序运行的实例.每一个进程都有自己独立的地址空间,一般

nginx 多进程 + io多路复用 实现高并发

一.nginx 高并发原理 简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发 二.nginx 多进程 启动nginx 解析初始化配置文件后会 创建(fork)一个master进程 之后 这个进程会退出 master 进程会 变为孤儿进程 由init进程托管.(可以通过python 或php 启动后创建子进程,然后杀死父进程得见子进程会由init进程托管) 如下图可以看到nginx master 进程由init(ppid 为1 )进程管理. maste

39.IO多路复用(用select实现伪并发)

IO多路复用 1.用select实现多端口被多客户端访问的多路复用伪并发 IO多路复用服务端:既读又写 # IO多路复用实现伪并发 用多个IO,可以监听多个文件句柄(socket对象)(一般是可以读了或者可以写了), # 一旦文件句柄出现变化,就可以感应到 # 对于原生的socket 只能处理一个请求,只能监听一个端口 # 1.如何让server端监听两个端口 import socket sk1 = socket.socket() sk1.bind(('127.0.0.1', 8001,)) s

Event Loop、函数式编程、IO多路复用、事件驱动、响应式、

IO多路复用.事件驱动.响应式概念类似或者一样 就是很多网络连接(多路),共(复)用少数几个(甚至是一个)线程. 连接很多的时候,不能每个连接一个线程,会耗尽系统内存的.线程也不能阻塞在任何一个连接上,等新的数据来,这样就不能及时响应其他连接发来的数据了:也不能用非阻塞方式,轮询所有的连接,这会浪费掉大量CPU时间:只能告诉系统,我对哪些连接感兴趣,有消息来的时候,通知我处理. IO多路复用: 一种在后端网络编程中的一种技术 IO多路复用机制详解    服务器,并发,"事件驱动"的本质

第15章 高并发服务器编程(2)_I/O多路复用

3. I/O多路复用:select函数 3.1 I/O多路复用简介 (1)通信领域的时分多路复用 (2)I/O多路复用(I/O multiplexing) ①同一线程,通过"拨开关"方式,来同时处理多个I/O流,哪个IO准备就绪就把开关拨向它.(I/O多路复用类似于通信领域中的"时分复用") ②通过select/poll函数可以实现IO多路复用,他们采用轮询的方式来监视I/O.而epoll是对select/poll的加强,它是基于事件驱动,epoll_ctl注册事件

基于Perfect用Swift语言编写Slack聊天机器人

基于Perfect用Swift语言编写Slack聊天机器人 本项目是专门为Slack聊天机器人定制的模板服务器. 完整的源代码下载在Github https://github.com/PerfectServers/SlackBot 在本项目模板中,一个聊天机器人可以加入授权频道,读取频道内所有用户发送的"曲奇"并记录在案,而且可以直接答复用户的有关曲奇饼干的问题. 预备知识 在您决定编译.测试或者部署您自己的基于Perfect软件框架体系的聊天机器人之前,以下基础知识??不可或缺??:

Python(七)Socket编程、IO多路复用、SocketServer

本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 功能: sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 参数一:地址簇 socket.AF_INET IPv4(默认)

Python----Socket编程、IO多路复用、SocketServer

本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 功能: sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 参数一:地址簇 socket.AF_INET IPv4(默认)