fd_set 函数讲解
#include<winsock.h>
函数原型:
int select(
int nfds,
fd_set* readfds,
fd_set* writefds.
fd_set* exceptfds,
const struct timeval* timeout
);
参数说明:
ndfs: 本参数忽略,仅起到兼容的作用
readfds: (可选指针)指向一组等待可读性检查的套接口
writefds: (可选指针)指向一组等待可写性检查的套接口
exceptfds:(可选指针)指向一组等待错误检查的套接口
timeout: select()最多等待时间,对堵塞操作则为NULL
函数作用说明:
本函数用于确定一个或者多个套接口的状态。对每一个套接口,调用者可查询它的可持续性、可写性以及错误状态信息。
使用fd_set结构来表示一组等待检查的套接口。在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足一定条件的套接口的数目。
Readfds:标识等待可读性检查的套接口
如果该套接口正处于监听listen()状态,则若有连接请求到达,该套接口便被标识为可读,这样一个accept()调用保证可以无阻塞完成。
Writefds:标识等待可写性检查的套接口
如果一个套接口正在connect()连接(非阻塞),可写性意味着连接顺利建立;
如果一个套接口并未处于connect()调用中,可写性意味着send()和sendto()调用将无阻塞完成,但是并未保证在多长时间内有效,特别是多线程的环境中
Exceptfds:标识等待带外数据存在性或意味着错误条件检查的接口;
也可以将以上三个参数都设置为NULL
在winsock.h头文件中一共定义了四个宏来操作描述子集。
FD_SETSIZE变量用于确定一个集合中最多有多少个描述符
FD_SETSIZE缺省值为64,可在包含winsock.h前用#define FD_SETSIZE 来改变该值
四个宏分别是:
FD_CLR(s,*set):从集合set中删除描述符
FD_ISSET(s,*set):若s为集合中的一员,非零;否则,为零
FD_SET(s,*set):向集合添加描述字s
FD_ZERO(s,*set):将set初始化为NULL
timeout参数控制select()完成的时间.
若timeout参数为空指针,则select()将一直阻塞到有一个描述字满足条件
否则,timeout指向一个timeval结构,其中指定了select调用在返回前(返回符合条件的套接口的数目)等待多长时间
如果timeval{0,0},则select立即返回,这可用于探询所选套接口的状态。
如果处于这种状态,则select()调用可认为是非阻塞的,且一切适用于非阻塞调用的假设都适用于它。
返回值:
select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字数;
如果超时则返回0 (意思就是说 已经超过timeval设置的时间,但是还没有满足条件的描述字);
否则返回SOCKET_ERROR错误,应用程序可以通过WSAGetLastError()获取相应错误代码。
#include
#include<winsock.h>
using namespace std;
int main()
{
SOCKET sock;
sock = socket(AF_INET,SOCK_STREAM,0); //TCP协议
//结构体 告诉sock应该在什么地方监听
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htonl(1111);
addr.sin_addr.s_addr = htonl(INADDR_ANY); //在本机上的所有ip上开始监听
bind(sock,(sockaddr*)&addr,sizeof(addr)); //绑定
listen(sock,5); //最大5个队列
//用来接收一个连接
SOCKET socka;
fd_set rfd; //描述符集 用来测试有没有一个可用的连接
FD_ZERO(&rfd); //先清空一个描述符集
struct timeval timeout;
timeout.tv_sec = 60;
timeout.tv_usec = 0;
unsigned long ul = 1;
ioctlsocket(sock, FIONBIO, &ul); //用非阻塞的连接
//使用select
/*
把sock放入要测试的描述符集就是sock放入了rfd里面,这样下一步调用select对rfd进行测试的时候就会测试sock
一个描述符集可以包含多个测试的描述符
*/
FD_SET(sock,&rfd);
if (select(sock+1,&rfd,0,0,&timeout) == 0) //第一参数要加1
{
// to do
}
if (FD_ISSET(sock,&rfd)) //有一个描述符满足条件 准备好了
{
socka = accept(sock,0,0); //一个用来读 一个用来写
//to do
}
system("pause");
return 1;
}