您的位置:首页 > 其它

IO多路复用之select

2016-05-26 20:29 351 查看
系统提供select函数来实现多路复用输入/输出模型。select函数让我们的程序监视多个文件描述符的状态变化。程序会停在select这里等待,直到被监视的文件描述符中有一个或多个发生了状态变化函数原型如下:
返回值:   成功返回就绪描述符的个数,超过timeout时间且没有任何事件发生返回0,失败返回-1
参数解释:nfds: 被监视的文件描述符中值最大描述符值加1(描述符是从0开始的,描述符0、1、2...nfds-1均将被测试)下面三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述符。如果对某一个的条件不感兴趣,就可以把它设为空指针。readfds: 读描述符集(所有关心读事件的描述符被用户添加进来),这个参数是个输入输出型参数writefds: 写描述符集exceptfds: 异常描述符集struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:void FD_CLR(int fd, fd_set *fdset); //把给定的文件描述符从指定集合删除 int FD_ISSET(int fd, fd_set *fdset); //判断指定描述符时候被加入了指定集合中(是否可读/可写) void FD_SET(int fd, fd_set *fdset); //把一个给定的文件描述符加入集合之中 void FD_ZERO(fd_set *fdset); //清空集合timeout: 设置一段时间(1)timeout=NULL时,当前进程一直等待,直到有一个描述符准备好I/O(永远等下去)(2)timeout指向timeval结构体且时间设置不为0时,当前进程挂起,如果到了设定的时间(timeout)还没有任何事件发生就返回0,进程继续执行后面的代码(等待一段固定时间)(3)timeout指向timeval结构体且时间设置为0时,不会等待,立即返回(根本不等待)struct timeval{ long tv_sec; //seconds long tv_usec; //microseconds };server_select.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define BLOCK 6

int fds[64];

void usage(char * proc)
{
printf("%s [ip] [port]\n",proc);
}

int create_sock(char *port,const char * inaddr)
{
//1.
int listenfd=socket(AF_INET,SOCK_STREAM,0);
if(listenfd<-1){
perror("listenfd");
exit(1);
}
//2.
struct sockaddr_in local;
local.sin_family=AF_INET;
int _port=atoi(port);
local.sin_port=htons(_port);
local.sin_addr.s_addr=inet_addr(inaddr);

struct linger lig;
int iLen;
lig.l_onoff=1;
lig.l_linger=0;
iLen=sizeof(struct linger);
setsockopt(listenfd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen);
//3.
if(bind(listenfd,(struct sockaddr*)&local,sizeof(local))<0){
perror("bind");
exit(2);
}
//4.
if(listen(listenfd,BLOCK)<0){
perror("listen");
exit(3);
}

return listenfd;
}

int main(int argc,char* argv[])
{
if(argc!=3){
usage(argv[0]);
exit(1);
}

//create listenfd
int listen_fd=create_sock(argv[2],argv[1]);

struct sockaddr_in client;
socklen_t len=sizeof(client);

int max_fd=listen_fd;

char buf[1024];

//initialize fds[]
int fds_num=sizeof(fds)/sizeof(fds[0]);
int i=0;
for(i=0;i<fds_num;++i){
fds[i]=-1;
}
fds[0]=listen_fd;

int done=0;
while(!done){
//define and initialize parameter of select()
fd_set readset;
FD_ZERO(&readset);
FD_SET(listen_fd,&readset);

fd_set writeset;
FD_ZERO(&writeset);

struct timeval timeout;
timeout.tv_sec=5;
timeout.tv_usec=0;

//
for(i=1;i<fds_num;++i){
if(fds[i]>0){
FD_SET(fds[i],&readset);
FD_SET(fds[i],&writeset);
if(fds[i]>max_fd)
max_fd=fds[i];
}
else
break;
}

switch(select(max_fd+1,&readset,&writeset,NULL,&timeout)){
//switch(select(max_fd+1,&readset,NULL,NULL,&timeout)){
case -1:
perror("select");
break;
case 0:
printf("timeout...\n");
break;
default:
for(i=0;i<fds_num;++i){
//listen_fd happen
if(fds[i]==listen_fd && FD_ISSET(fds[i],&readset)){
int connfd=accept(listen_fd,(struct sockaddr*)&client,&len);
if(connfd<0){
perror("accept");
break;
}else{
printf("get a connect...\n");
for(i=0;i<fds_num;++i){
if(fds[i]==-1){
fds[i]=connfd;
break;
}
}
}
}
//normal event happen
else if(fds[i]>0 && FD_ISSET(fds[i],&readset)){
ssize_t _size=read(fds[i],buf,sizeof(buf)-1);
if(_size<0){
perror("read");
}else if(_size==0){//client closed
printf("client shutdown...\n");
close(fds[i]);
fds[i]=-1;
continue;
}else{
buf[_size]='\0';
printf("client# %s",buf);
if(FD_ISSET(fds[i],&writeset)){
if(write(fds[i],buf,sizeof(buf)-1)<0){
perror("write");
}
}
}

}
else{
}
}
break;
}
}

return 0;
}
client_select.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>

void usage(char *proc)
{
printf("%s [ip] [port]\n",proc);
}

int creat_socket()
{
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd<0){
perror("socket");
exit(1);
}
return fd;
}

int main(int argc,char* argv[])
{
if(argc!=3){
usage(argv[0]);
exit(1);
}

int fd=creat_socket();

int _port=atoi(argv[2]);
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(_port);
inet_aton(argv[1],&addr.sin_addr);
socklen_t addrlen=sizeof(addr);
if(connect(fd,(struct sockaddr*)&addr,addrlen)<0){
perror("connect");
exit(2);
}

char buf[1024];
while(1){
memset(buf,'\0',sizeof(buf));
printf("Please Enter:");
fgets(buf,sizeof(buf)-1,stdin);
if(send(fd,buf,sizeof(buf)-1,0)<0){
perror("send");
continue;
}

//回显
ssize_t _size=recv(fd,buf,sizeof(buf)-1,0);
if(_size>0){
buf[_size]='\0';
printf("echo->%s\n",buf);
}
}

return 0;
}
运行结果:(可把服务器端和客户端结果对比起来看)服务器端客户端
select模型的缺点:1.我的机器测试
printf("fd_set=%d\n",sizeof(fd_set));  值为128,说明能监视的描述符的最大值为128*8=1024个
2.每次都要把所关心的描述符重新添加一次,即从用户态拷贝到内核态(因为select返回时没有发生事件的描述符将被清空),如果描述符较多的时候,开销是很大的3.当select返回的时候,即使只有一个事件发生了,也要把整个集遍历一次,同样,如果描述符较多的时候,开销也很大
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  socket select 多路复用