【Linux】多路复用之—select

一:多路复用之——select

int select(int nfds,fd_set  *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout)

  1、参数:   (1)nfds:需要监视的文件描述符数目;

                     (2)readfds、writefds、ecceptfds:对应于需要检测的可读文件描述符集合、可写文件描述符集合、异常文件描述符集合;

                     (3)timeout:NULL:没有timeout,一直阻塞,直到某个文件描述符发生事件

                                                     0:仅检测文件描述符的状态,然后立即返回

                                            特定值:等待timeout时间,如果事件没有发生,超时返回

  2、宏:提供处理这三种描述符词组的方式

(1):FD_CLR(int fd,fd_set* set)清除文件描述符集中相关fd的位

   (2):FD_ISSET(int fd,fd_set* set)测试文件描述符集中与fd相关的事件是否发生

   (3):FD_SET(int fd,,fd_set* set)设置文件描述符集中相关fd的位

   (4):FD_ZERO(int fd,fd_set* set)清除文件描述符集中的全部位

  3、timeout:

   struct timeout{

long tv_sec         秒

long tv_usec      微妙

}

4、返回值:

(1)成功:返回文件描述符集中状态改变的文件描述符个数

(2)0:超过timeout时间

(3)-1:失败

5、理解select模型:

取fd_set长度为1字节,fd_set中的每一bit位对应一个文件描述符fd,则1字节长的fd_set最多可以对应8个fd

(1)执行fd_set set,DF_ZERO(&set),则set用位表示为0000 0000

(2)若fd=5,执行fd_set(fd,&set),后set变为0001 0000

(3)若再加入fd=1,fd=2,则set变为0001 0011

(4)执行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2都发生,则select返回,set变为0000 0011  注意:没有事件发生的fd=5被清空

6、select模型的特点:

(1)可监控的文件描述符个数取决于sizeof(fd_set)的值;

(2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd。一是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断;二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(先FD_ZERO),扫描array的同时取得fd最大值maxfd,用于select第一个参数;

(3)select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有事件发生)。

7、select模型的缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,当fd很多时,开销会很大;

(2)在每次调用select时,都要遍历fd,开销大;

(3)select支持的文件描述符数目太少,默认是1024,不能处理海量数据。


my_select.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/select.h>
#define _PORT_ 8080
#define _BACK_LOG_ 5
#define _MAX_FD_NUM_ 32
int array_fd[_MAX_FD_NUM_];
int startup()
{
	int sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock<0){
		perror("socket");
		exit(1);
	}
	struct sockaddr_in local;
	local.sin_family=AF_INET;
	local.sin_port=htons(_PORT_);
	local.sin_addr.s_addr=htonl(INADDR_ANY);
	socklen_t len=sizeof(local);
	if(bind(sock,(struct sockaddr*)&local,len)<0){
		perror("bind");
		exit(2);
	}
	if(listen(sock,_BACK_LOG_)<0){
		perror("listen");
		exit(3);
	}
	return sock;
}
int main()
{
	int listen_sock=startup();
	struct sockaddr_in client;
	socklen_t len=sizeof(client);
	fd_set read_set;
	int i=0;
	int max_fd=listen_sock;
	array_fd[0]=listen_sock;
	for(i=1;i<_MAX_FD_NUM_;i++){
		array_fd[i]=-1;
	}
	while(1){
		FD_ZERO(&read_set);
		for(i=0;i<_MAX_FD_NUM_;i++){
			if(array_fd[i]>0){
				FD_SET(array_fd[i],&read_set);
				if(max_fd<array_fd[i]){
					max_fd=array_fd[i];
				}
			}
		}
		struct timeval time_out={3,0};
		switch(select(max_fd+1,&read_set,NULL,NULL,&time_out)){
			case 0://timeout
				printf("timeout...\n");
				break;
			case -1://error
				perror("select");
				break;
			default:
			    {
				for(i=0;i<_MAX_FD_NUM_;i++){
					if(array_fd[i]<0){
						continue;
					}else if(array_fd[i]==listen_sock && FD_ISSET(array_fd[i],&read_set)){
						int new_sock=accept(array_fd[i],(struct sockaddr*)&client,&len);
						if(new_sock<0){
							continue;
						}
						printf("get a new connect...\n");
						for(i=0;i<_MAX_FD_NUM_;i++){
							if(array_fd[i]==-1){
								array_fd[i]=new_sock;
								break;
							}
						}
						if(i==_MAX_FD_NUM_){
							printf("array_fd is full\n");
							close(new_sock);
						}
					}else{
						for(i=1;i<_MAX_FD_NUM_;i++){
							if(array_fd[i]>0 && FD_ISSET(array_fd[i],&read_set)){
								char buf[1024];
								memset(buf,'\0',sizeof(buf)-1);
								ssize_t _size=read(array_fd[i],buf,sizeof(buf)-1);
								if(_size==0){
									printf("client close...\n");
									close(array_fd[i]);
									array_fd[i]=-1;
								}else if(_size<0){
								}else{
									printf("client: %s\n",buf);
								}
							}
						}
					}
				}
			    }
				break;
		}
	}
	close(listen_sock);
	return 0;
}


client.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#define _PORT_ 8080
int main()
{
	int read_fd=0;
	int write_fd=1;
	fd_set read_set;
	fd_set write_set;
	int max_fd=0;
	int sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock<0){
		perror("socket");
		exit(1);
	}
	struct sockaddr_in remote;
	remote.sin_family=AF_INET;
	remote.sin_port=htons(_PORT_);
	remote.sin_addr.s_addr=inet_addr("192.168.0.146");
	if(connect(sock,(struct sockaddr*)&remote,sizeof(remote))<0){
		perror("connect");
		exit(2);
	}
	if(sock>read_fd){
		max_fd=sock;
	}else{
		max_fd=read_fd;
	}
	while(1){
		FD_ZERO(&read_set);
		FD_ZERO(&write_set);
		FD_SET(read_fd,&read_set);
		FD_SET(sock,&write_set);
		switch(select(max_fd+1,&read_set,&write_set,NULL,NULL)){
			case 0:
				printf("timeout...\n");
				break;
			case -1:
				perror("select");
				break;
			default:
				{
					if(FD_ISSET(read_fd,&read_set)){
						char buf[1024];
						memset(buf,'\0',sizeof(buf));
						ssize_t _size=read(read_fd,buf,sizeof(buf)-1);
						if(_size>0){
							buf[_size]='\0';
							printf("echo: %s\n",buf);
						}
						if(FD_ISSET(sock,&write_set)){
							send(sock,buf,strlen(buf),0);
						}
					}
				}
				break;
		}
	}
	return 0;
}

Makefile

.PHONY:all
all:my_select client
my_select:my_select.c
	gcc -o $@ $^
client:client.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -rf my_select client



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值