I/O 多路复用

版权声明:请在征得作者同意的情况下,可以进行非盈利性引用。引用请注明出处:“作者:慕华思弦 转载地址” 字样,以尊重作者的劳动成果,并保持良好的版权意识。 https://blog.csdn.net/Superman___007/article/details/82932996

I/O类型:
    接下来我们将介绍几种常见的I/O模型及其区别
        阻塞I/O:blocking I/O(如果没有信息,则阻塞)
        非阻塞I/O:nonblocking I/O
        多路复用I/O:I/O multiplexing (select and poll)
        信号I/O:signal driven I/O (SIGIO)
        异步I/O:asynchronous I/O (the POSIX aio_functions)

I/O多路复用:I/O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个I/O请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降底了系统的维护工作量,节省了系统资源。
   API:select 、poll、epoll的解决方案

针对如下场合就需要用到IO复用:
    当客户处理多个描述符时候
    一个同时处理多个套接字的时候
    如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字的时候
    如果一个服务器既要处理TCP又要处理UDP
    如果一个服务器要处理多个服务或者多个协议的时候
    总结:如果一个线程中有多路径阻塞I/O时,就可以用多路复用

Select:该函数会等待多个I/O事件(比如读就绪,写)的任何一个发生,并且只要有一个网络事件发生,select线程就会执行。如果没有任何一个事件发生则阻塞。原型如下:
                int select ( int nfds , fd_set *readfds , fd_set *writefds , fd_set *exceptfds , struct timeval *timeout ) ;

                nfds:最大的监哨的文件描述符
                readfs:读的文件描述符
                writes 写的文件描述符
                exceptfds错误输出文件描述符
                timeout:在指定时间内
                struct timeout
               {
                         long misec;
                         long sec;
                }

               特点:如果监哨的文件描述符没有状态改变(没有读写改变),则会阻塞。否则会唤醒并通知。每监哨一次时,重新设置文件描述符集;返回时必须判断原因    返回值:-1  失败   errno错误的状态
    

1、创建集合:
        fd_set set
2、添加描述符到集合
        FD_SET
    从文件描述符集中删除描述符
        FD_CLR
    判断文件描述符中是否发生改变:
        FD_ISSET    
                某文件描述符状态发生改变>0   失败==0
    清空描述符集:
        FD_ZERO

   多路I/O 的 服务器实现 : 

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
//UDP--服务器
//4关闭套接字
int main()
{
//1创建套接字
	int sock=socket(AF_INET,SOCK_DGRAM,0);
	if(sock<0)
	{
		perror("socket fail");
		return -1;
	}
//2绑定套接字
	struct sockaddr_in myaddr;
	myaddr.sin_family		=AF_INET;
	myaddr.sin_port			=htons(7979);
	myaddr.sin_addr.s_addr		=INADDR_ANY;
	if(bind(sock,(struct sockaddr*)&myaddr,sizeof(myaddr))<0)
	{
		perror("socket fail");
		return -1;
	}
//3收发消息
	int ilen=0;
	char buf[100]="";
	struct sockaddr_in caddr;
	socklen_t addrlen=sizeof(caddr);
	while(1)
	{
		ilen=recvfrom(sock,buf,99,0,(struct sockaddr*)&caddr,&addrlen);
		if(ilen<=0)
			break;
		buf[ilen]='\0';
		//输出
		printf("%s\n",buf);
		//返回消息
		sendto(sock,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
	}
//关闭
	close(sock);
	return 0;
}

   主要是在客户端的代码 , 使用select 函数实现 :

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/select.h>
#include<errno.h>
int main()
{
//创建套接字
	int sock=socket(AF_INET,SOCK_DGRAM,0);
	if(sock<0)
	{
		perror("fail\n");
		return -1;
	}
//绑定
//发送
	//创建文件描述符集
	fd_set rset;
	FD_ZERO(&rset);
	int maxfd=STDIN_FILENO>sock?STDIN_FILENO:sock;	//键盘的描述符 0
	//地址结构体:服务器
	struct sockaddr_in saddr;
	bzero(&saddr,sizeof(saddr));	//清空
	saddr.sin_family		=AF_INET;
	saddr.sin_port			=htons(7979);
	saddr.sin_addr.s_addr		=inet_addr("192.168.8.138");
	char buf[100]="";
	int ilen=0;
	while(1)
	{
		//设置文件描述符集:从键盘上读取  从套接字读取
		FD_SET(STDIN_FILENO,&rset);
		FD_SET(sock,&rset);
		//多路I/O复用
		if(-1==select(maxfd+1,&rset,NULL,NULL,NULL)&&errno!=EINVAL)
		{
			perror("select fail");
			break;
		}
		if(FD_ISSET(STDIN_FILENO,&rset)>0)    //从键盘上读取
		{
			read(STDIN_FILENO,buf,99);
			//发送
			sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&saddr,sizeof(saddr));
		}
		if(FD_ISSET(sock,&rset)>0)            //从套接字读取
		{
			//读取
			ilen=recv(sock,buf,99,0);
			if(ilen<=0)
				break;
			buf[ilen]='\0';
			printf("收到:%s\n",buf);
		}
	}
//关闭
	close(sock);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Superman___007/article/details/82932996