select
int select(int nfds, fd_set * readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
功能:多路IO复用。
参数:
nfds: readfsd\ writefds\exceptfds三个位图中使用的最大的文件描述符号+1.
readfds: 位图(128Byte,1024bit),需要监听的读事件集合
writefds: 位图(128Byte,1024bit),需要监听的写事件集合
exceptfds: 位图(128Byte,1024bit),需要监听的异常事件集合
timeout:超时时长,传NULL:如果没有监听的事件没有发生,就一直阻塞等待;传非0:表示超时时间,在规定的时间内如果监听的时间都没有发生,select 返回0;传0:表示非阻塞。调用的时候查看一遍监听集合,如果没有任何事件发生,select立即返回0。
返回值:
-1,出错并设置errno;0:表示没有任何监听的事发生;>0:监听的三个集合发生的事件和。
优点:
跨平台,开销小。
缺点:
受限数据结构的限制,能够监听的文件描述符上限1024个。
轮询监听机制,在活跃用户量小的时候监听效率比较低。
大量的用户和内核空间的数据拷贝。
利用select单进程实现多可客户端连接的服务器代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<arpa/inet.h>
#include<sys/select.h>
#define SERPORT 8000
#define SERIP "IP地址"
int main(int argc, char* argv[])
{
int lfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in seraddr,cliaddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(SERPORT);
int dst;
inet_pton(AF_INET,SERIP,(void*)&dst);
seraddr.sin_addr.s_addr = dst;
int ret = bind(lfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
listen(lfd,64);
socklen_t addrlen = sizeof(cliaddr);
char buf[1024];
int maxfd;
fd_set rset, aset;
FD_ZERO(&aset);
FD_SET(lfd,&aset);
maxfd = lfd;
int i = 0;
int cfd;
char clip[32];
while(1){
rset = aset;
int sret = select(maxfd + 1, &rset, NULL, NULL, NULL);
if(sret<0){
perror("select error");
exit(1);
}
if(FD_ISSET(lfd, &rset)){
cfd = accept(lfd,(struct sockaddr*)&cliaddr,&addrlen);
if(cfd<0){
perror("accept error");
exit(1);
}
//网络字节序整形IP地址转化成一个本地字节序点分十进制的字符窜IP
inet_ntop(AF_INET,&cliaddr.sin_addr,clip,sizeof(clip));
printf("clien IP=%s,PORT=%d connect ok\n",clip,ntohs(cliaddr.sin_port));
FD_SET(cfd, &aset);
if(cfd>maxfd){
maxfd = cfd;
}
if(--sret ==0 ){
continue;
}
}
for(i = lfd+1;i<maxfd+1;i++){
if(FD_ISSET(i, &rset)){
int rr = read(i,buf,sizeof(buf));
if(rr < 0){
perror("read error");
exit(1);
}
else if(rr == 0){
//客户端断开连接
FD_CLR(i, &aset);
close(i);
printf("客户端断开连接\n");
}
write(STDOUT_FILENO,buf,rr);
write(i,buf,rr);
if(--sret == 0){
break;
}
}
}
}
return 0;
}