select模型的缺点:
1、最大并发数限制。由于一个进程所打开的文件描述符fd有限,有FD_SETSIZE设置,默认最大为1024/2048,因此并发数被限制。(poll中克服了这一问题)
2、效率问题。 每次进行select调用都会采用轮询fd方式(线性扫描全部的fd)。时间复杂度O(n)
3、内核/应用空间内存拷贝。select在解决将fd消息(内核空间)传递给用户空间时采用了内存拷贝方法。
简单通过select函数实现I/O复用完成并发服务器的编写:
功能:将select函数运用在服务器中,实现I/O复用
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 5678
#define IP "10.21.12.13"
int main(int argc,char *argv[])
{
int sockfd,listenfd;
int err;
int client_addrlen,bytes;
fd_set globe_nfds,current_nfds;
int nfds,i;
char buf[128];
struct sockaddr_in server_addr, client_addr;
client_addrlen = sizeof(struct sockaddr_in);
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd == -1)
{
printf("creat socket error,errno = %d\n",errno);
return -1;
}
memset(&server_addr,0,sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);//32bit IPv4 Address
err = bind(listenfd ,(struct sockaddr *)&server_addr,sizeof(struct sockaddr));
if(err == -1)
{
printf("bind error,errno is %d\n",errno);
close(listenfd);
return -1;
}
printf("bind success!\n");
if(listen(listenfd ,10) == -1)
{
printf("listen error,errno is %d\n",errno);
close(listenfd);
return -1;
}
printf("listening...\n");
FD_ZERO(&globe_nfds);
FD_SET(listenfd,&globe_nfds);
nfds = listenfd;
while(1)
{
current_nfds = globe_nfds;
if(select(nfds+1,¤t_nfds,NULL,NULL,NULL)<0)
{
perror("select error.\n");
printf("errno:%d\n",errno);
close(listenfd);
return -1;
}
for(i=0;i<= nfds;i++)
{
if(FD_ISSET(i,¤t_nfds))
{
if(i == listenfd)
{
sockfd = accept(listenfd,(struct sockaddr*)&client_add, &client_addrlen);
if(sockfd<0)
{
peror("accept error.\n");
close(listenfd);
return -1;
}
nfds = nfds>sockfd?nfds:sockfd;
FD_CLR(listenfd,¤t_nfds);
FD_SET(sockfd,&globe_nfds);
}
else
{
bytes = recv(i,buf,128,0);
if(bytes<0)
{
perror("recv data error.");
printf("errornum:%d\n",errno);
close(i);
return -1;
}
if(bytes == 0)
{
printf("client closed!\n");
FD_CLR(i,&globe_nfds);
close(i);
continue;
}
printf("recv data from client :%s\n",buf);
send(i,buf,strlen(buf),0);
bzero(buf,128);
}
}
}
}
}
升级版:提高效率的一种方式
> 功能:将select函数运用在服务器中,实现I/O复用
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 5678
#define IP "10.21.12.13"
int main(int argc,char *argv[])
{
int sockfd,listenfd;
int err;
int client_addrlen,bytes;
fd_set globe_nfds,current_nfds;
int nfds,i;
char buf[128];
char clientfd[FD_SETSIZE];
printf("FD_SETSIZE=%d\n",FD_SETSIZE);
struct sockaddr_in server_addr, client_addr;
client_addrlen = sizeof(struct sockaddr_in);
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd == -1)
{
printf("creat socket error,errno = %d\n",errno);
return -1;
}
memset(&server_addr,0,sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);//32bit IPv4 Address
err = bind(listenfd ,(struct sockaddr *)&server_addr,sizeof(struct sockaddr));
if(err == -1)
{
printf("bind error,errno is %d\n",errno);
close(listenfd);
return -1;
}
printf("bind success!\n");
if(listen(listenfd ,10) == -1)
{
printf("listen error,errno is %d\n",errno);
close(listenfd);
return -1;
}
printf("listening...\n");
FD_ZERO(&globe_nfds);
FD_SET(listenfd,&globe_nfds);
nfds = listenfd;
//初始化套接字数组
for(i = 0 ;i < FD_SETSIZE ; i ++)
clientfd[i] = -1 ;
while(1)
{
current_nfds = globe_nfds;
if(select(nfds+1,¤t_nfds,NULL,NULL,NULL)<0)
{
perror("select error.\n");
printf("errno:%d\n",errno);
close(listenfd);
return -1;
}
//若是监听套接字则等待来自客户端的连接
if(FD_ISSET(listenfd,¤t_nfds))
{
sockfd = accept(listenfd,(struct sockaddr*)&client_addr,&client_addrlen);
if(sockfd<0)
{
perror("accept error.\n");
close(listenfd);
return -1;
}
nfds = nfds>sockfd?nfds:sockfd;
FD_CLR(listenfd,¤t_nfds);
FD_SET(sockfd,&globe_nfds);//将新建立的通信连接套接字加入套接字集
for(i = 0 ; i < nfds ;i ++)
{
if(-1 == clientfd[i])
{
clientfd[i] = sockfd;
break;
}
}
}
for(i = 0 ; i <= nfds ; i++ )
{
if(clientfd[i] == -1)
{
continue;
}
if(FD_ISSET(clientfd[i], ¤t_nfds))
{
bytes = recv(clientfd[i],buf,128,0);
if(bytes<0)
{
perror("recv data error.");
printf("errornum:%d\n",errno);
close(clientfd[i]);
return -1;
}
if(bytes == 0)
{
printf("client %d has closed!\n",clientfd[i]);
FD_CLR(clientfd[i],&globe_nfds);
clientfd[i] = -1;
close(clientfd[i]);
continue;
}
printf("recv data from client :%s\n",buf);
send(clientfd[i],buf,strlen(buf),0);
bzero(buf,128);
}
}
}
}