select()函数用法
select()函数是实现服用服务器端的一种方法。这里介绍Linux环境下select()函数的用法,Windows下的大同小异。select函数的功能select函数可以同时监视多个文件描述符,并且可以监视三种事件。一旦某个文件描述符所指的对象发生了相应事件,就可以进行相应的处理。监视的三种事件:(1)是否有对象需要接受数据。(2)是否有对象需要传输数据。(3)是否有对象发生了异常。select
select()函数是实现服用服务器端的一种方法。这里介绍Linux环境下select()函数的用法,Windows下的大同小异。
select函数的功能
select函数可以同时监视多个文件描述符,并且可以监视三种事件。一旦某个文件描述符所指的对象发生了相应事件,就可以进行相应的处理。
监视的三种事件:
(1)是否有对象需要接受数据。(2)是否有对象需要传输数据。(3)是否有对象发生了异常。
select函数的调用过程:
(1)设置文件描述符,指定监视范围,设置超时。
(2)调用select函数。
(3)查看调用结果。
下面一一说明。
设置文件描述符
先介绍fd_set数组变量,该结构是存有0和1的位数组。如果该数组第0位是1,代表文件描述符为0的对象是监视对象。操作该数组的方法和普通数组不一样,一般通过以下宏来对该数组进行操作。
FD_ZERO(fd_set *fdset); 将fd_set所指变量的所有位置0。
FD_SET(int fd,fd_set *fdset); 将fdset所指变量的第fd位置1,此时fd文件描述符所指的对象成为监视对象。
FD_CLR(int fd,fd_set *fdset); 将fdset所指变量的第fd位置0。
FD_ISSET(int fd,fd_set *fdset); 如果fd文件描述符所指对象是监视对象(即第fd位为1),则返回true。
select函数参数
int select(int maxfd,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);
参数含义:
maxfd:监视对象文件描述符的对象,一般就是所监视的最大文件描述符+1。
readset:readset中为1的文件描述符,将监视它们是否需要接受数据,即上述的事件(1)。
writeset:writeset中为1的文件描述符,将监视它们是否需要传输数据,即上述的事件(2)。
exceptset:同理。
timeout:最大等待时间。
返回值:发生错误时返回-1,超时时返回0。如果发生监视的事件,返回相应的文件描述符。
timeout的结构如下:
struct timeval
{
long tv_sec; //秒
long tv_usec; //微秒
}
查看结果
select函数返回正整数时,参数中三个fdset地址所指的变量中为位全部置0,除了发生事件的文件描述符。因此,可以通过调用FD_ISSET得知是哪个文件描述符发生了事件。
一个小例子
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<sys/time.h>
#include<sys/select.h>
#include<unistd.h>
using namespace std;
const int BUF_SIZE=30;
int main()
{
fd_set reads;
char buf[BUF_SIZE];
timeval timeout;
FD_ZERO(&reads);
FD_SET(0,&reads); //0是指控制台输入
while(1)
{
fd_set tmp(reads); //每次调用完select,除了发生事件的文件描述符为1,其他全部置0
//因此每次循环都要复制一遍
timeout.tv_sec=5; //设置超时时间为5秒
timeout.tv_usec=0;
int result(select(1,&tmp,0,0,&timeout)); //调用select
if(result==-1)
{
cout<<"select() error!\n";
break;
}
else if(result==0) cout<<"Time-out!\n";
else
{
if(FD_ISSET(0,&tmp)) //判断是否是文件描述符0发生事件,即是否需要读数据
{
int len(read(0,buf,BUF_SIZE));
buf[len]=0;
cout<<"Message from console:"<<buf<<endl;
}
}
}
return 0;
}
利用select实现复用服务器端
sever.cpp
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<sys/select.h>
using namespace std;
const int BUF_SIZE=30;
void errorHandling(char *message);
int main()
{
int serverSock,clientSock,fdMax,port;
sockaddr_in serverAddr,clientAddr;
fd_set reads;
socklen_t addrSize;
char buf[BUF_SIZE];
cout<<"Input port:";
cin>>port;
serverSock=socket(PF_INET,SOCK_STREAM,0);
memset(&serverAddr,0,sizeof(serverAddr));
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
serverAddr.sin_port=htons(port);
if(bind(serverSock,(sockaddr *)&serverAddr,sizeof(serverAddr))==-1)
errorHandling("bind() error!");
if(listen(serverSock,5)==-1) errorHandling("listen() error!");
FD_ZERO(&reads);
FD_SET(serverSock,&reads); //设置serverSock为监视对象,监视是否需要接受数据
fdMax=serverSock;
while(1)
{
fd_set copy(reads);
timeval timeout;
timeout.tv_sec=5;
timeout.tv_usec=0;
int flag(select(fdMax+1,©,0,0,&timeout));
if(flag==-1) errorHandling("select() error!");
else if(flag==0) continue;
for(int i=0;i<=fdMax;i++) //循环遍历,看哪个文件描述符发生了事件
{
if(FD_ISSET(i,©))
{
if(i==serverSock) //serverSock需要接受数据,说明有新的客户端请求连接
{
addrSize=sizeof(clientAddr);
clientSock=accept(serverSock,(sockaddr *)&clientAddr,&addrSize);
FD_SET(clientSock,&reads); //将新的套接字纳入监视对象中
fdMax=max(fdMax,clientSock); //更新fdMax
cout<<"connected client:"<<clientSock<<endl;
}
else //如果不是serverSock,那么就肯定是用来跟客户端通信的套接字发生了事件,即上面的clientSock,对于不同的客户端,通过accept得到的clientSock是不同的
{
int len(read(i,buf,BUF_SIZE));
if(len==0)
{
FD_CLR(i,&reads);
close(i);
cout<<"closed client:"<<i<<endl;
}
else write(i,buf,len);
}
}
}
}
close(serverSock);
return 0;
}
void errorHandling(char *message)
{
cerr<<message<<endl;
exit(1);
}
client.cpp
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
using namespace std;
const int BUF_SIZE=1024;
const int MAXN=30;
void errorHandling(char *message);
int main()
{
int sock,len,port;
char message[BUF_SIZE],ip[MAXN];
sockaddr_in serverAddr;
cout<<"Input ip:";
cin>>ip;
cout<<"Input port:";
cin>>port;
sock=socket(PF_INET,SOCK_STREAM,0);
if(sock==-1) errorHandling("socket() error!");
memset(&serverAddr,0,sizeof(serverAddr));
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr(ip);
serverAddr.sin_port=htons(port);
if(connect(sock,(sockaddr*)&serverAddr,sizeof(serverAddr))==-1)
errorHandling("connect() error!");
else cout<<"Connected....\n";
while(1)
{
cout<<"Input message(Q to quit):\n";
cin>>message;
if(!strcmp(message,"q")||!strcmp(message,"Q"))
break;
write(sock,message,strlen(message));
len=read(sock,message,BUF_SIZE-1);
message[len]=0;
cout<<"Message from server:"<<message<<endl;
}
close(sock);
return 0;
}
void errorHandling(char *message)
{
cerr<<message<<endl;
exit(1);
}
运行截图:
更多推荐
所有评论(0)