select函数介绍:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds :监视的文件描述符数量
readfds :将是否有可读的数据注册到fd_set型变量中
writefds :将是否有可写的数据注册到fd_set型变量中
exceptfds:将是异常的数据注册到fd_set型变量中
timeout :超时(为防止无休止的阻塞,传递超时信息)
select用来检测三种监视项的变化情况,根据监视项的三个fd_set类型变量,
分别向其注册文件描述符信息。
fd_set类型变量相关介绍:
void FD_CLR(int fd, fd_set *set);//将描述符fd从集合set中去除
int FD_ISSET(int fd, fd_set *set);//集合set中描述符fd是否发生变化
void FD_SET(int fd, fd_set *set);//将描述符fd添加到集合set中
void FD_ZERO(fd_set *set);//将集合中的变量全部清0
超时结构体struct timeval介绍:
struct timeval{
_time_t tv_sec;//秒
_suseconds_t tv_usec;//毫秒
};
(Linux下查看该结构体的原型可以输入指令 vi -t timeval,前提是需要先安装ctags)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#define Max 1024
#define Error -1
static int sfd = 0;
const ushort port = 49999;
//服务器的初始化
int init_server()
{
//建立流式套接字
sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0){
perror("socket");
goto SET_ERR;
}
//服务端的地址结构(ipv4)
//htons进行大小端转化
//INADDR_ANY表示任意ip地址,系统提供的宏
struct sockaddr_in server_addr = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr = {
.s_addr = htonl(INADDR_ANY),
},
};
//绑定地址
if(bind(sfd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){
perror("bind");
goto SET_ERR;
}
//设置监听套接字,允许最大连接的套接字
if(listen(sfd,10) < 0){
perror("listen");
goto SET_ERR;
}
return sfd;
SET_ERR:
close(sfd);
return Error;
}
int main(int argc, const char *argv[])
{
int fd = init_server();
int maxfd = fd;
int cfd;
int i = 0;
//客户端地址信息
struct sockaddr_in client_addr;
int len = sizeof(client_addr);
//一般需要两个集合
fd_set rfds,tempfds;
//清空集合
FD_ZERO(&rfds);
FD_ZERO(&tempfds);
//将套接字fd放到集合rfds中
FD_SET(fd,&rfds);
//设置超时信息,为5s
struct timeval time = {
.tv_sec = 5,
.tv_usec = 0,
};
struct timeval temptv;
printf("waiting for connect...\n");
while(1){
tempfds = rfds;
temptv = time;
//进行select轮询
int arg = select(maxfd+1,&tempfds,NULL,NULL,&temptv);
if(arg < 0){
perror("select");
goto SET_ERR;
}else if(arg == 0){
//超时
printf("time out...\n");
}
//循环遍历集合
for(i=0;i<=maxfd;i++){
if(FD_ISSET(i,&tempfds)){
if(i == fd){//如果描述符就绪了,表示有客户端进行连接
cfd = accept(fd,(struct sockaddr*)&client_addr,&len);
if(cfd < 0){
perror("accept");
goto SET_ERR;
}
printf("the client ip is %s\n",inet_ntoa(client_addr.sin_addr));
FD_SET(cfd,&rfds);//将得到的客户端描述符添加到集合中
maxfd = (maxfd<cfd)?(cfd):(maxfd);//更新最大的文件描述符
}else{//客户端发送数据
char buf[Max] = {0};
memset(buf,0,sizeof(buf));
//注意:这里接收数据的时候,第一个参数不能填fd,
//如果填的fd,则只能接收到一个客户端发送的数据
size_t nByte = recv(i,buf,sizeof(buf),0);
if(nByte < 0){
perror("recv");
close(cfd);
return Error;
}else if(nByte == 0){
printf("the client is disconnect...\n");
FD_CLR(i,&rfds);//客户端退出之后,将其文件描述符从集合中去除
}else{
printf("%s\n",buf);
}
}
}
}
}
return 0;
SET_ERR:
close(fd);
return Error;
}