select函数
函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
【参数列表】:
nfds:监听的所有文件描述符的最大描述符+1(内核采取轮询的方式);
readfds:读文件描述监听集合;
writefds:写文件描述符集合;
exceptfds:异常文件描述符集合;
timeout:大于0:设置监听超时时长;NULL:阻塞监听;0:非阻塞监听;
【返回值】:
大于0:所欲监听集合中,满足对应时间的总数
0:没有满足的
-1:出错error
【注意】:
第二个、第三个、第四个参数的类型为fd_set ,内核为操作这种集合定义了四个函数:
void FD_CLR(int fd,fd_set * set); //将一个文件描述符从集合中移除
void FD_SET(int fd,fd_set * set); //将监听的文件描述符,添加到监听集合中
void FD_ZERO(fd_set * set) //清空一个文件描述符集合
int FD_ISSET(int fd,fd_set * set); //判断一个文件描述符是否在一个集合中,返回值:在1,不在0
【示例】:
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define N 5
int Initserver()
{
//创建网络套接字
int serfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==serfd)
{
perror("socket failed\n\r");
return -1;
}
printf("套接字创建成功\n\r");
//绑定服务器ip地址和端口号
struct sockaddr_in seraddr;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_port=htons(9999);
seraddr.sin_family=AF_INET;
seraddr.sin_addr.s_addr=inet_addr("192.168.43.26");
socklen_t addrlen=sizeof(seraddr);
int ret=bind(serfd,(struct sockaddr *)&seraddr,addrlen);
if(ret<0)
{
perror("bind failed\n\r");
close(serfd);
return -1;
}
printf("ip地址和端口号绑定成功\r\n");
//设置服务器为监听模式
ret=listen(serfd,N);
if(ret<0)
{
perror("listen failed\r\n");
close(serfd);
return -1;
}
printf("服务器设置为监听模式成功\r\n");
return serfd;
}
int main()
{
int serfd=Initserver();
fd_set rfds; //创建读文件集合,因为是服务器,所以只需要读就可以了
FD_ZERO(&rfds); //清空集合
FD_SET(serfd,&rfds); //将我们的套接字描述符,放到读文件集合中
int maxFd=serfd; //首先我们将我们创建的套接字描述符最为基量最大,后续开始比较
while(1)
{
fd_set tmprfds=rfds; //定义了一个中间变量读文件集合
int ret=select(maxFd+1,&tmprfds,NULL,NULL,NULL);
if(ret<0)
{
perror("select failed\r\n");
close(serfd);
return -1;
}
int i=0;
for(i=0;i<maxFd+1;i++)
{
if(FD_ISSET(i,&tmprfds))
{
if(serfd==i)
{
struct sockaddr_in cliaddr;
memset(&cliaddr,0,sizeof(cliaddr));
socklen_t cliaddrlen=sizeof(cliaddr);
int confd= accept(serfd,(struct sockaddr *)&cliaddr,&cliaddrlen);
if(confd<0)
{
perror("accept failed\r\n");
close(serfd);
return -1;
}
printf("建立连接成功\r\n");
printf("ip地址是:%s,端口号是%u\r\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
FD_SET(confd,&rfds);
if(confd>maxFd)
{
maxFd=confd;
}
}
}
}
}
return 0;
}
client.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
//创建套接字
int cli_sock=socket(AF_INET,SOCK_STREAM,0);
if(cli_sock<0)
{
perror("socket failed\n");
return -1;
}
puts("socket success\n");
//客户端可以不绑定ip地址和端口号,系统自动分配
puts("系统自动分配ip地址和端口号\n");
struct sockaddr_in ser_addr={0};
socklen_t addrlen=sizeof(ser_addr);
ser_addr.sin_family=AF_INET;
ser_addr.sin_addr.s_addr=inet_addr("192.168.43.26");
ser_addr.sin_port=htons(9999);
if(connect(cli_sock,(struct sockaddr *)&ser_addr,addrlen)<-1)
{
perror("connect failed\n");
return -1;
close(cli_sock);
}
puts("connect success\n");
close(cli_sock);
return 0;
}
【结果】:
select是一个阻塞函数,代码只演示了IO多路复用创建的并发服务器,大家可以参考,具体的代码,可以借鉴https://blog.csdn.net/weixin_44228194/article/details/99301004,里面是完整代码。