int select(int maxfdp, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数:
(1)maxfdp: 当前最大描述符数+1
(2)readfds: 指向一个套接字集合,用于检测其可读性
(3)writefds: 指向一个套接字集合,用于检测其可写性
(4)exceptfds:指向一个套接字集合,用于检测错误
(5)timeout: select函数的超时时间
结构体如下:
struct timeval{
time_t tv_sec;
time_t tv_usec;
};
它可以使select处于三种状态:
(a)若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,
一定等到监视文件描述符集合中某个文件描述符发生变化为止;
(b)若将时间值设为0秒0微秒,就变成一个纯粹的非阻塞函数,
不管文件描述符是否有变化,都立刻返回继续执行,
文件无变化返回0,有变化返回一个正值;
(c)timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,
超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回。
fd_set其实这是一个数组的宏定义,实际上是一long类型的数组,
每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,
建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,
由此来通知执行了select()的进程哪个句柄可读。
=============================
ps:
linux下的fd_set结构和windows下的fd_set结构还是有所不同的,
Windows系统下的fd_count和fd_array[]是不可以使用的
系统提供了以下宏可以在不同的套接字环境中提高移植性
(1)FD_CLR(s,*set):从集合set中删除套接字s
(2)FD_ISSET(s,*set):若套接字s是集合中的一员,返回值非0,否则是0
(3)FD_SET(s,*set) :将套接字s添加到集合中
(4)FD_ZERO(*set):将set集合初始化为空集NULL
select模型流程(重点)
select函数返回的是就绪socket的数量,用户需要自定义数组来保存就绪的socket并且处理。
fd_set这个结构只是负责监听,但是要注意需要我们自己人为的将传入时的集合(inset)和传出时的集合(outset)分离开来。
inset:代表需要监视的文件描述符,将他们置为1
outset:代表将就绪的socket返回时依旧保留为1,其余的就是置为0
如下图所示:
客户端代码
#include
#include
#include
#include
#include
#include
#include
#include
#define ServerIP "192.168.43.8"
#define ServerPort 27015
int main()
{
int ret;
char sendbuf[128];
bzero(sendbuf,sizeof(sendbuf));
struct sockaddr_in addrServer;
bzero(&addrServer,sizeof(addrServer));
addrServer.sin_family = AF_INET;
addrServer.sin_port = htons(ServerPort);
inet_pton(AF_INET,ServerIP,&addrServer.sin_addr.s_addr);
//创建socket
int clientfd = socket(AF_INET,SOCK_STREAM,0);
if(clientfd ==-1){
printf("socket error\n");
return -1;
}
//发起连接请求
ret = connect(clientfd,(struct sockaddr*)&addrServer,sizeof(addrServer));
if(ret == -1){
printf("connect error\n");
close(clientfd);
return -1;
}
printf("connect successful.....\n");
printf("Please input sendbuf:");
scanf("%s",sendbuf);
//发送数据
ret = send(clientfd,sendbuf,sizeof(sendbuf),0);
if(ret > 0){
printf("host send:%s\n",sendbuf);
}
close(clientfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
//网络初始化
char recvbuf[1024];
char ip[16];
int ret;
int maxfd;
int clientfdArr[1024];
struct sockaddr_in addrserver,addrclient;
bzero(&addrserver,sizeof(addrserver));
bzero(&recvbuf,sizeof(recvbuf));
bzero(&ip,sizeof(ip));
addrserver.sin_family = AF_INET;
addrserver.sin_port = htons(27015);
addrserver.sin_addr.s_addr = htonl(INADDR_ANY);
//创建socket
int serverfd = socket(AF_INET,SOCK_STREAM,0);
if(serverfd == -1){
printf("create socket error\n");
return 1;
}
//绑定
ret = bind(serverfd,(struct sockaddr*)&addrserver,sizeof(addrserver));
if(ret == -1)
{
printf("bind error\n");
close(serverfd);
return 1;
}
//监听
ret = listen(serverfd,128);
if(ret == -1)
{
printf("listen error\n");
close(serverfd);
return 1;
}
printf("Select Server Runing.....\n");
//启用select模型
fd_set inset,outset;
FD_ZERO(&inset);
FD_SET(serverfd,&inset); //将serverfd放入readset集合中
//初始化socket描述符数组
for(int i = 0; i < 1024; ++i)
clientfdArr[i] = -1;
maxfd = serverfd;
//select循环调用
while(1)
{
outset = inset; //将传入监听集合赋给传出监听集合
ret = select(maxfd+1,&outset,NULL,NULL,NULL);
if(ret > 0)
{
printf("select successful...\n");
printf("就绪的数量为:%d\t事件:某客户端请求连接.....\n",ret);
//serverfd就绪
if(FD_ISSET(serverfd,&outset))
{
socklen_t size = sizeof(addrclient);
int clientfd = accept(serverfd,(struct sockaddr*)&addrclient,&size);
//将新的clientfd加入监听集合和socket描述符数组中
FD_SET(clientfd,&inset);
for(int i = 0; i < 1024; ++i)
if(clientfdArr[i] == -1){
clientfdArr[i] = clientfd;
break;
}
maxfd = clientfd > maxfd ? clientfd : maxfd;
}
//clientfd就绪,数据传输
else
{
for(int i = 0; i < 1024; ++i)
{
if(clientfdArr[i] != -1)
{
if(FD_ISSET(clientfdArr[i],&outset))
{
ret = recv(clientfdArr[i],recvbuf,sizeof(recvbuf),0);
if(ret > 0)
{
printf("recv data is:%s\n",recvbuf);
continue;
}
else if(ret == 0)
{
printf("client normal closed...\n");
close(clientfdArr[i]);
FD_CLR(clientfdArr[i],&inset);
clientfdArr[i] = -1;
break;
}
else{
printf("recv error\n");
close(clientfdArr[i]);
FD_CLR(clientfdArr[i],&inset);
clientfdArr[i] = -1;
break;
}
}
}
}
}
}
else
{
printf("select error\n");
break;
}
}
close(serverfd);
return 0;
}