首先介绍一下select 函数的功能、参数、返回值。
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set); //将某个文件描述符从监视表中移除
int FD_ISSET(int fd, fd_set *set);//判断文件描述符是否准备就绪
void FD_SET(int fd, fd_set *set);//将文件描述符放入监视表中
void FD_ZERO(fd_set *set); //清空监视表
select 函数监控文件描述符的状态,当某一个或几个文件描述符准备就绪,则可以执行相应的I/O操作。这个函数最多可以监视1024个文件描述符。
参数:
nfds:这个参数就是程序所监视的最大的文件描述符+1。(置于为什么要加一,是由于内核中的代码在写的时候就是这样设定的,比如for循环,for(i=1;i<10;i++),i<10的时候是循环9次,i<=10的时候,循环10次,内核中设计这个函数的时候估计就是差了一个等号)。
readfds:这个参数是select 函数监视的读文件描述符的集合。如果不监视传NULL
writefds:这个参数是select 函数监视的写文件描述符的集合。如果不监视传NULL
exceptfds:这个参数是select 函数监视的异常文件描述符的集合。如果不监视传NULL
timeout: 设置函数的超时时间
基于select 函数实现的TCP服务器的基本思路就是,每次有客户端连接的时候,将accetp 返回的文件描述符放入被监视的文件描述符集合中去,当客户端发信息的时候,对应的文件描述符准备就绪就执行相应的操作。当客户端离开或者断开连接,就将文件描述符从集合中删除。
接下来直接上代码。上面没有叙述的部分会在代码中以注释的方式讲解。
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <unistd.h>
#define ERRLOG(msg) do{\
printf("file:%s func:%s line:%d\n",__FILE__,__func__,__LINE__);\
perror(msg);\
exit(-1);\
}while(0)
int main(int argc, const char *argv[])
{
int clientfd;//客户端连接后产生的文件描述符
int sockfd;
if(-1 == (sockfd=socket(AF_INET,SOCK_STREAM,0)))//创建流式套接字
ERRLOG("socket error");
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[2]));
socklen_t serveraddr_len=sizeof(serveraddr);//填充服务器网络信息结构体
if(-1 == bind(sockfd,(struct sockaddr *)&serveraddr,serveraddr_len))//将套接字和网络信息结构体绑定
ERRLOG("bind error");
if(-1 == listen(sockfd,5))//将套接字设置为被监听状态
ERRLOG("listen error");
fd_set readfds;
fd_set writefds;
fd_set exceptfds;//创建文件描述符监视表
fd_set readfds_temp;
fd_set writefds_temp;
fd_set exceptfds_temp;//创建文件描述符监视表副本,因为每次有文件描述符准备就绪号之后,select会将表中没有准备就绪的文件描述符清除
FD_ZERO(&readfds_temp);
int maxfd=0;
FD_ZERO(&readfds);
FD_SET(sockfd,&readfds);
int i=0;
int ret=0;
maxfd=maxfd>sockfd?maxfd:sockfd;
readfds_temp=readfds;
int nbytes;
char buf[128]={0};
while(1){
readfds_temp=readfds;
if(-1 == (ret = select(maxfd+1,&readfds_temp,NULL,NULL,NULL))){
ERRLOG("select error");
}else{
for(i=3;i<maxfd+1&&ret!=0;i++){//select 的返回值表示准备就绪的文件描述符的个数
if(FD_ISSET(i,&readfds_temp)){
if(i == sockfd){//如果准备就绪的文件描述符是sockfd,则说明有新的用户过来连接
if(-1 == (clientfd = accept(sockfd,NULL,NULL)))
ERRLOG("accept error");
FD_SET(clientfd,&readfds);
maxfd=maxfd>clientfd?maxfd:clientfd;//每次有新的文件描述符加入就更新最大的文件描述符
printf("client[%d] link...\n",clientfd);
}else{//说明有人发消息
memset(buf,0,sizeof(buf));
if(-1 == (nbytes = recv(i,buf,sizeof(buf),0))){
ERRLOG("recv error");
}else if(nbytes == 0){
printf("client[%d] disconnect...\n",i);
FD_CLR(i,&readfds);
continue;
}
printf("do work...");//根据代码要实现的功能填写代码
strcpy(buf,"completed!!");
send(i,buf,sizeof(buf),0);
}
ret--;//每一次执行完操作就将准备就绪的文件描述符的个数减1
}
}
}
}
/*your code*/
return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)