Linux中select函数学习及实例笔记Word格式.docx
《Linux中select函数学习及实例笔记Word格式.docx》由会员分享,可在线阅读,更多相关《Linux中select函数学习及实例笔记Word格式.docx(9页珍藏版)》请在冰豆网上搜索。
select函数可以指示内核等待多个事件中的任一个发生,仅在一个或多个事件发生,或者等待一个足够的时间后才唤醒进程!
select函数的原型如下:
#include
<
sys/types.h>
#include<
sys/time.h>
intselect(intmaxfdp1,fd_set*readset,fd_set*writeset,fd_setexcpetset,conststructtimeval*timeout);
返回值:
准备好的描述符的正数目0---超时-1---出错!
其中最后一个参数是一个结构体的指针,它表示等待内核中的一组描述符任一个准备好需要花费多久的时间!
其中timeval指定了秒数和微秒数。
structtimeval{
longtv_sec;
//秒数
longtv_usec;
//微秒数
};
将timeout设置为空指针时,会永远等待下去,等待固定的时间:
如果timeout指向的timeval中的具体的值时,会等待一个固定的时间,不等待立刻返回,这时timeval中的tv_sec和tv_usec为0.
select有三个可能的返回值。
1.正常情况下返回就绪的文件描述符个数;
2.经过了timeout时长后仍无设备准备好,返回值为0;
3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。
4.如果出错,返回-1并设置相应的errno。
EBADF文件描述词为无效的或该文件已关闭
EINTR此调用被信号所中断
EINVAL参数n为负值。
ENOMEM核心内存不足
中间的三个参数readsetwriteset和excpetset指定我们要让内核测试读、写、异常条件所需的描述字!
函数select使用描述字集,它一般是一个整型的数组,每个数中的每一位代表一个描述符!
系统提供了4个宏对描述符集进行操作:
#include<
sys/select.h>
//设置文件描述符集fdset中对应于文件描述符fd的位
voidFD_SET(intfd,fd_set*fdset);
//清除文件描述符集fdset中对应于文件描述符fd的位(设置为0)
voidFD_CLR(intfd,fd_set*fdset);
//清除文件描述符集fdset中的所有位(既把所有位都设置为0)
voidFD_ZERO(fd_set*fdset);
//在调用select后使用FD_ISSET来检测文件描述符集fdset中对应于文件描述符fd的位是否被设置。
voidFD_ISSET(intfd,fd_set*fdset);
例如下面一段代码:
fd_setreadset;
FD_ZERO(&
readset);
FD_SET(5,&
FD_SET(33,&
则文件描述符集readset中对应于文件描述符6和33的相应位被置为1,如图1所示:
再执行如下程序后:
FD_CLR(5,&
则文件描述符集readset对应于文件描述符6的相应位被置为0,如图2所示:
通常,操作系统通过宏FD_SETSIZE来声明在一个进程中select所能操作的文件描述符的最大数目。
一般情况下被定义为1024.一个整数占4个字节,既32位,那么就是用包含32个元素的整数数组来表示文件描述符集。
我们可以在头文件中修改这个值来改变select使用的文件描述符集的大小,但是需要注意的是:
必须重新编译内核才能使修改后的值有效。
如果我们对其中的个一不感兴趣的话,可以设置为空指针。
如果我们把三个都设为空指针,就实现了一个比sleep更准确的定时器。
注意select函数的第一个参数maxfdp1,是所有加入集合的句柄值的最大那个值还要加1。
比如我们的描述符为145,那么maxfdp1就为6.描述符从0开始。
当我们调用函数时,指定我们关心的描述符集,当返回时,指示那些描述符已经准备好了。
怎么样才算准备好呢!
下面列出几种情况:
下列四个条件中的任何一个满足时,套接口准备好读:
(1)套接口接收缓冲区中的数据字节数大于等于套接口接收缓冲区低潮限度的当前值。
可以通过SO_REVILOAT来设置此低潮限度。
(2)连接的读这一半关闭,也就是接收了FIN的TCP连接,
(3)套接口是一个监听套接口且已完成的连接数为非0.
(4)有一个套接口错误等处理。
下列三个条件中的任一个满足时,套接口准备好写:
(1)套接口发送缓冲区的可用空间字节娄大于等于套接口发送缓冲区低潮限度的当前值且或者(i)套接口已连接,或者(ii)套接口不要求连接。
(2)连接的写这一半关闭,对这样的套接口写操作将产生信号SIGPIEP。
(3)有一个套接口错误待处理。
下面是select函数的一个例子,主要参考网上的例子,并进行的适当的改变!
并增加了客户端的程序。
修改不fd[i]数组,可是实现动态的管理,也解决了,当一个客户不断的断开再连接时,服务器也断开的情况。
网址参考如下:
/*使用select函数可以以非阻塞的方式和多个socket通信。
程序只是演示select函数的使用,即使某个连接关闭以后也不会修改当前连接数,连接数达到最大值后会终止程序。
1.程序使用了一个数组fd,通信开始后把需要通信的多个socket描述符都放入此数组
2.首先生成一个叫sock_fd的socket描述符,用于监听端口。
3.将sock_fd和数组fd中不为0的描述符放入select将检查的集合fdsr。
4.处理fdsr中可以接收数据的连接。
如果是sock_fd,表明有新连接加入,将新加入连接的socket描述符放置到fd。
*/
//select_server.c
stdio.h>
stdlib.h>
unistd.h>
errno.h>
string.h>
sys/socket.h>
netinet/in.h>
arpa/inet.h>
#defineMYPORT1234//连接时使用的端口
#defineMAXCLINE5//连接队列中的个数
#defineBUF_SIZE200
intfd[MAXCLINE];
//连接的fd
intconn_amount;
//当前的连接数
voidshowclient()
{
inti;
printf("
clientamount:
%d\n"
conn_amount);
for(i=0;
i<
MAXCLINE;
i++)
[%d]:
%d"
i,fd[i]);
}
\n\n"
);
intmain(void)
intsock_fd,new_fd;
//监听套接字连接套接字
structsockaddr_inserver_addr;
//服务器的地址信息
structsockaddr_inclient_addr;
//客户端的地址信息
socklen_tsin_size;
intyes=1;
charbuf[BUF_SIZE];
intret;
//建立sock_fd套接字
if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==-1)
perror("
setsockopt"
exit
(1);
//设置套接口的选项SO_REUSEADDR允许在同一个端口启动服务器的多个实例
//setsockopt的第二个参数SOLSOCKET指定系统中,解释选项的级别普通套接字
if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&
yes,sizeof(int))==-1)
setsockopterror\n"
server_addr.sin_family=AF_INET;
//主机字节序
server_addr.sin_port=htons(MYPORT);
server_addr.sin_addr.s_addr=INADDR_ANY;
//通配IP
memset(server_addr.sin_zero,'
\0'
sizeof(server_addr.sin_zero));
if(bind(sock_fd,(structsockaddr*)&
server_addr,sizeof(server_addr))==-1)
binderror!
\n"
if(listen(sock_fd,MAXCLINE)==-1)
listenerror!
listenport%d\n"
MYPORT);
fd_setfdsr;
//文件描述符集的定义
intmaxsock;
structtimevaltv;
conn_amount=0;
sin_size=sizeof(client_addr);
maxsock=sock_fd;
while
(1)
//初始化文件描述符集合
fdsr);
//清除描述符集
FD_SET(sock_fd