select()函数

Posted 拙园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了select()函数相关的知识,希望对你有一定的参考价值。

  select(),确定一个或多个套接口的状态,本函数用于确定一个或多个套接口的状态,对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息,用fd_set结构来表示一组等待检查的套接口,在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足条件的套接口的数目。有一组宏可用于对fd_set的操作,这些宏与Berkeley Unix软件中的兼容,但内部的表达是完全不同的。

简述

确定一个或多个套接口的状态,如需要则等待。
#include <sys/select.h>
int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);
nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。

注释

readfds参数标识等待可读性检查的套接口。如果该套接口正处于监听listen()状态,则若有连接请求到达,该套接口便被标识为可读,这样一个accept()调用保证可以无阻塞完成,对其他套接口而言,可读性意味着有排队数据供读取。或者对于SOCK_STREAM类型套接口来说,相对于该套接口的虚套接口已关闭,于是recv()recvfrom()操作均能无阻塞完成,writefds参数标识等待可写性检查的套接口。如果一个套接口正在connect()连接(非阻塞),可写性意味着连接顺利建立。如果套接口并未处于connect()调用中,可写性意味着send()sendto()调用将无阻塞完成。〔但并未指出这个保证在多长时间内有效,特别是在多线程环境中〕。
exceptfds参数标识等待带外数据存在性或意味错误条件检查的套接口,请注意如果设置了SO_OOBINLINE选项为假FALSE,则只能用这种方法来检查带外数据的存在与否,对于SO_STREAM类型套接口,远端造成的连接中止和KEEPALIVE错误都将被作为意味出错。如果套接口正在进行连接connect()(非阻塞方式),则连接试图的失败将会表现在exceptfds参数中。
如果对readfds、writefds或exceptfds中任一个组类不感兴趣,可将它置为空NULL。
在socket.h头文件中共定义了四个宏来操作描述字集。FD_SETSIZE变量用于确定一个集合中最多有多少描述字(FD_SETSIZE缺省值为64,可在包含socket.h前用#define FD_SETSIZE来改变该值)。对于内部表示,fd_set被表示成一个套接口的队列,最后一个有效元素的后续元素为INVAL_SOCKET。宏为:FD_CLR(s,*set):从集合set中删除描述字s。FD_ISSET(s,*set):若s为集合中一员,非零;否则为零。FD_SET(s,*set):向集合添加描述字s。FD_ZERO(*set):将set初始化为空集NULL。
timeout参数控制select完成的时间。若timeout参数为空指针,则select将一直阻塞到有一个描述字满足条件,否则的话,timeout指向一个timeval结构,其中指定了select调用在返回前等待多长时间。如果timeval为{0,0},则select立即返回,这可用于探询所选套接口的状态,如果处于这种状态,则select调用可认为是非阻塞的,且一切适用于非阻塞调用的假设都适用于它,举例来说,阻塞钩子函数不应被调用,且套接口实现不应yield。

返回值

select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError获取相应错误代码。
当返回为-1时,所有描述符集清0。
当返回为0时,表示超时。
当返回为正数时,表示已经准备好的描述符数。
select()返回后,在3个描述符集里,依旧是1的位就是准备好的描述符。这也就是为什么,每次用select后都要用FD_ISSET的原因。

错误代码

WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:套接口实现检测到网络子系统失效。
WSAEINVAL:超时时间值非法,
WSAEINTR:通过一个WSACancelBlockingCall()来取消一个阻塞的
WSAEINPROGRESS:一个阻塞的套接口调用正在运行中。
WSAENOTSOCK:描述字集合中包含有非套接口的元素。

调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//下面是示例代码:
//代码是服务器TCP模型,采用多路复用的select函数实现了循环的监听并接受客户端的功能,其中也包含了上传下载的功能*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<sys/ioctl.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/types.h>
#include<dirent.h>
 
int main()
{
    struct sockaddr_in seraddr,cliaddr;
    int listenfd,connfd,fd1,fd2,n,m,l,port;
    char user[20],buf[4096];
    fd_set  readfds,tmpfds;//岗哨监控集合
    socklen_t addrlen;
    DIR *dr;
    struct dirent *file;
 
    printf("请输入需要设定的服务器名称:");
    scanf("%s",user);
     
    printf("请输入需要设定的服务器端口:");
    scanf("%d",&port);
    getchar();
 
    if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        perror("创建失败");
       exit(-1);
    }
     
     
 
    /*开始设定服务器的参数地址类型,IP,PORT*/
    memset(&seraddr,0,sizeof(seraddr));//将服务器的初值空间清空,防止转化过程有影响
    seraddr.sin_family=AF_INET;
    seraddr.sin_port=htons(port);//将得到的本地端口转换为网络字节序
    seraddr.sin_addr.s_addr=htonl(INADDR_ANY);//将得到的ip地址字符串转换为网络字节序的ip地址数值
 
    if((bind(listenfd,(struct sockaddr*)&seraddr,sizeof(seraddr))<0))
    {
        perror("绑定失败");
       exit(-1);
    }
    printf("绑定创建\n");
    if((connfd=listen(listenfd,50))<0)
    {
        perror("监听失败");
        exit(-1);
    }
    printf("开始监听\n");
    FD_ZERO(&readfds);//初始化文件集
    FD_SET(listenfd,&readfds);//将需要监视的listenfd放入readfds集中
 
    while(1)//循环监听
    {
        int nread,n;
        tmpfds=readfds;//将监视集传递给临时的监视集中,防止后续的操作修改了监视集
        if(select(FD_SETSIZE,&tmpfds,NULL,NULL,NULL)<0)//设置监视,监视tmpfds内的子fd变化,发生变化的将会被保留在tmpfds中
          {
           perror("监视未成功");
            exit(-1);
         }
         
         for(fd1=0;fd1<FD_SETSIZE;fd1++)//循环找在最大范围内的fd1
        {
           if(FD_ISSET(fd1,&tmpfds))//查找是否fd在tmpfds里面
            {
              if(fd1==listenfd)//判定fd等于监听fd,即监听fd在监视过程中出现变化被发现
              {
                  addrlen=sizeof(cliaddr);
                 connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&addrlen);//开始接收客户
                 FD_SET(connfd,&readfds);//将connfd加入监视集,监视接入的变化
                 printf("接入新的连接\n");
              }
              else
                  {
                      ioctl(fd1,FIONREAD,&nread);//测试在fd中还有nread个字符需要读取
                      if(nread==0)//如果需要读取的数据为0,则关闭检测出来的fd1,并且从监视中移除
                    {
                     close(fd1);
                     FD_CLR(fd1,&readfds);
                      printf("移除\n");
                    }
                      else//如果可读数据不为0,则读出来
                    {
                        int i;
                        char *p=NULL,*q=NULL;
                      n=read(fd1,buf,nread);
                      buf[n]=0;
                      p=buf;
                       
                       
                          if((strncmp(p,"-get",4)==0))
                          {  
                             q=p+5;
                             printf("客户下载文件>%s",q);
                             if((fd2=open(q,O_RDONLY))<0)
                                 perror("打开文件错误");
                             
                             while((m=read(fd2,buf,4096))>0)
                             {
                                 write(connfd,buf,m);
                                  
                             }
                             bzero(buf,sizeof(buf));
                             close(fd1);
                             close(fd2);
                             FD_CLR(fd1,&readfds);
                           
                          }
                           
                          if((strncmp(p,"-up",3)==0))
                          {  
                             q=p+4;
                             printf("客户上传文件%s\n",buf+4);
                             if((fd2=open(q,O_CREAT | O_WRONLY | O_APPEND ,0666))<0)
                             {
                                perror("打开文件写入失败");
                                  
                             }
                              
                             while((m=read(connfd,buf,128))>0)
                             
                                 printf("%s",buf);
                                 write(fd2,buf,m);
                             }
                             bzero(buf,sizeof(buf));
                             close(fd1);
                             close(fd2);
                             FD_CLR(fd1,&readfds);
                           
                          }
                           
                          if((strncmp(p,"-ls",3)==0))
                          {  
                             q=p+4;
                             printf("客户查看文件……");
                             if((dr=opendir(q))==NULL)
                                 perror("打开目录失败");
                             while((file=readdir(dr))!=NULL)
                             {
                                   printf("%s    ",file->d_name);
                                  write(connfd,file->d_name,sizeof(file->d_name));
 
                             }
                             close(fd1);
                             close(connfd);
                             closedir(dr);
                             FD_CLR(fd1,&readfds);
                           
                          }
                       
                      printf("从客户收取的信息:%s\n",buf);
                    }
 
 
                  }
 
            }//end if 0
             
          
        }//end for 0
 
     
    }//end while0
 
 
 
 
 
 
    exit(0);
}//end main

相关参考

WSAAsyncSelect(),accept(),connect(), recv(), recvfrom(),send()。

事件用法

概述
触发每一个匹配元素的select事件
这个函数会调用执行绑定到select事件的所有函数,包括浏览器的默认行为。可以通过在某个绑定的函数中返回false来防止触发浏览器的默认行为。
返回值
jQuery
示例
触发所有input元素的select事件:
jQuery 代码:
$("input").select();

以上是关于select()函数的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段8——声明函数

Discuz代码片段

常见的代码片段

sql数据库截取字符串函数

d3.select(this)不能用箭头函数

使用从循环内的代码片段中提取的函数避免代码冗余/计算开销