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

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

背景介绍

如果服务器要同时处理网络上的套接字连接请求和本地的标准输入命令请求,那么如果我们使用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);
		}
	}
}

测试部分


zzw@zzw-ThinkPad-Edge-E430c:~$ telnet localhost 9999
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
hello



zzw@zzw-ThinkPad-Edge-E430c:~/doc_main/CProgram/Concurrency$ ./select.o 9999
client connected!server received 7 bytes

最后强调一下,这里是同步的IO问题,并非异步。


关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看