close
文章出處

select函數

該函數允許進程指示內核等待多個事件中的任何一個發生,并僅在有一個或是多個事件發生或經歷一段指定的時間后才喚醒它。我們調用select告知內核對哪些描述字(就讀、寫或異常條件)感興趣以及等待多長時間。我們感興趣的描述字不局限于套接口,任何描述字都可以使用select來測試。

select函數原型:

#include<sys/select.h>
#include<sys/time.h>
int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval * timeout);
返回:就緒描述字的正數目,0——超時,-1——出錯

select函數的參數介紹:maxfd表示待測試的描述字個數,其值應該為最大的描述字+1,中間的readset,writeset,exceptionset指定我們要讓內核測試讀、寫、異常條件的描述字,最后一個參數告知內核等待所指定描述字中的任何一個就緒可花多長時間。

timeval結構:

struct timeval {
long tv_sec; //seconds
long tv_usec ; //microseconds
}

timeval參數有三種可能值:1、NULL:代表永遠等待下去,相當于完全阻塞。2、一個固定的值,代表等待一段固定的時間。3、timeval的屬性值為0,表示根本不等待,檢查描述字之后立即返回,也就是說事非阻塞的。

fd_set結構:

fd_set結構表示一個描述字集。它典型的應該以一個整數數組來表示,其中每個整數中的每一位對應一個描述字。關于fd_set有以下四個宏:

void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */
void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */
void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset */
int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */

select函數修改由指針readset,writeset,exceptionset所指向的描述字集,因而這三個參數都是值-結果參數。也就是說,在select函數執行過程中,會修改其中的值。調用該函數時,我們指定關心的描述字的值,該函數返回時,結果指示哪些描述字已就緒。該函數返回后,我們使用FD_ISSET來測試fd_set數據類型中的描述字。描述字集中任何與未就緒的描述字對應的位返回時均清為0.為此,每次重新調用select函數中,我們都得再次把所有描述字集合中的所關心的位置為1。這也是在稍候的通信例子里,我們設置resset和allset兩個集合的原因所在。

select函數返回某個套接口就緒的條件:

select函數的通信例子:一個簡單的TCP回射服務器程序

SelectServer.c: 使用select機制的服務器端程序

 

  1 #include <stdio.h>
2 #include <string.h>
3 #include <arpa/inet.h>
4 #include <netinet/in.h>
5 #include <sys/socket.h>
6 #include <sys/select.h>
7
8 const static int MAXLINE = 1024;
9 const static int SERV_PORT = 10001;
10
11 int main1()
12 {
13 int i , maxi , maxfd, listenfd , connfd , sockfd ;
14 /*nready 描述字的數量*/
15 int nready ,client[FD_SETSIZE];
16 int n ;
17 /*創建描述字集合,由于select函數會把未有事件發生的描述字清零,所以我們設置兩個集合*/
18 fd_set rset , allset;
19 char buf[MAXLINE];
20 socklen_t clilen;
21 struct sockaddr_in cliaddr , servaddr;
22 /*創建socket*/
23 listenfd = socket(AF_INET , SOCK_STREAM , 0);
24 /*定義sockaddr_in*/
25 memset(&servaddr , 0 ,sizeof(servaddr));
26 servaddr.sin_family = AF_INET;
27 servaddr.sin_port = htons(SERV_PORT);
28 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
29
30 bind(listenfd, (struct sockaddr *) & servaddr , sizeof(servaddr));
31 listen(listenfd , 100);
32 /*listenfd 是第一個描述字*/
33 /*最大的描述字,用于select函數的第一個參數*/
34 maxfd = listenfd;
35 /*client的數量,用于輪詢*/
36 maxi = -1;
37 /*init*/
38 for(i=0 ;i<FD_SETSIZE ; i++)
39 client[i] = -1;
40 FD_ZERO(&allset);
41 FD_SET(listenfd, &allset);
42
43 for (;;)
44 {
45 rset = allset;
46 /*只select出用于讀的描述字,阻塞無timeout*/
47 nready = select(maxfd+1 , &rset , NULL , NULL , NULL);
48 if(FD_ISSET(listenfd,&rset))
49 {
50 clilen = sizeof(cliaddr);
51 connfd = accept(listenfd , (struct sockaddr *) & cliaddr , &clilen);
52 /*尋找第一個能放置新的描述字的位置*/
53 for (i=0;i<FD_SETSIZE;i++)
54 {
55 if(client[i]<0)
56 {
57 client[i] = connfd;
58 break;
59 }
60 }
61 /*找不到,說明client已經滿了*/
62 if(i==FD_SETSIZE)
63 {
64 printf("Too many clients , over stack .\n");
65 return -1;
66 }
67 FD_SET(connfd,&allset);//設置fd
68 /*更新相關參數*/
69 if(connfd > maxfd) maxfd = connfd;
70 if(i>maxi) maxi = i;
71 if(nready<=1) continue;
72 else nready --;
73 }
74
75 for(i=0 ; i<=maxi ; i++)
76 {
77 if (client[i]<0) continue;
78 sockfd = client[i];
79 if(FD_ISSET(sockfd,&rset))
80 {
81 n = read(sockfd , buf , MAXLINE);
82 if (n==0)
83 {
84 /*當對方關閉的時候,server關閉描述字,并將set的sockfd清空*/
85 close(sockfd);
86 FD_CLR(sockfd,&allset);
87 client[i] = -1;
88 }
89 else
90 {
91 buf[n]='\0';
92 printf("Socket %d said : %s\n",sockfd,buf);
93 write(sockfd,buf,n); //Write back to client
94 }
95 nready --;
96 if(nready<=0) break;
97 }
98 }
99
100 }
101 return 0;
102 }


Client.c: 簡單的客戶端程序

 1 #include <stdio.h>
2 #include <string.h>
3 #include <arpa/inet.h>
4 #include <netinet/in.h>
5 #include <sys/socket.h>
6
7 #define MAXLINE 1024
8 int main()
9 {
10 int sockfd ,n;
11 char buf [MAXLINE];
12 sockfd = socket(AF_INET,SOCK_STREAM ,0);
13 struct sockaddr_in servaddr;
14 memset(&servaddr, 0 ,sizeof(servaddr));
15 servaddr.sin_family = AF_INET;
16 servaddr.sin_port = htons(10001);
17 inet_pton( AF_INET ,"127.0.0.1" , &servaddr.sin_addr ) ;
18
19 connect(sockfd,(struct sockaddr *)&servaddr , sizeof(servaddr));
20 while(1)
21 {
22 printf("type some words ...\n");
23 scanf("%s",buf);
24 write(sockfd,buf,sizeof(buf));
25 n = read(sockfd,buf,MAXLINE);
26 printf("%d bytes received \n ",n);
27 buf[n] = '\0';
28 printf("%s\n",buf);
29 }
30 close(sockfd);
31 return 0;
32 }


總結:

在本文中,介紹了select函數在I/O復用中相關內容,并給出了一個典型的TCP反射程序的樣例,使用select可以處理多個客戶端的并發需求。在下一篇文章中,將會重點介紹另一種常見的I/O復用函數poll的使用。


不含病毒。www.avast.com
arrow
arrow
    全站熱搜

    AutoPoster 發表在 痞客邦 留言(0) 人氣()