unix下网络编程之I/O复用(二)
select函數(shù)
該函數(shù)允許進(jìn)程指示內(nèi)核等待多個(gè)事件中的任何一個(gè)發(fā)生,并僅在有一個(gè)或是多個(gè)事件發(fā)生或經(jīng)歷一段指定的時(shí)間后才喚醒它。我們調(diào)用select告知內(nèi)核對哪些描述字(就讀、寫或異常條件)感興趣以及等待多長時(shí)間。我們感興趣的描述字不局限于套接口,任何描述字都可以使用select來測試。
select函數(shù)原型:
#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);
返回:就緒描述字的正數(shù)目,0——超時(shí),-1——出錯
select函數(shù)的參數(shù)介紹:maxfd表示待測試的描述字個(gè)數(shù),其值應(yīng)該為最大的描述字+1,中間的readset,writeset,exceptionset指定我們要讓內(nèi)核測試讀、寫、異常條件的描述字,最后一個(gè)參數(shù)告知內(nèi)核等待所指定描述字中的任何一個(gè)就緒可花多長時(shí)間。
timeval結(jié)構(gòu):
struct timeval {long tv_sec; //seconds
long tv_usec ; //microseconds
}
timeval參數(shù)有三種可能值:1、NULL:代表永遠(yuǎn)等待下去,相當(dāng)于完全阻塞。2、一個(gè)固定的值,代表等待一段固定的時(shí)間。3、timeval的屬性值為0,表示根本不等待,檢查描述字之后立即返回,也就是說事非阻塞的。
fd_set結(jié)構(gòu):
fd_set結(jié)構(gòu)表示一個(gè)描述字集。它典型的應(yīng)該以一個(gè)整數(shù)數(shù)組來表示,其中每個(gè)整數(shù)中的每一位對應(yīng)一個(gè)描述字。關(guān)于fd_set有以下四個(gè)宏:
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函數(shù)修改由指針readset,writeset,exceptionset所指向的描述字集,因而這三個(gè)參數(shù)都是值-結(jié)果參數(shù)。也就是說,在select函數(shù)執(zhí)行過程中,會修改其中的值。調(diào)用該函數(shù)時(shí),我們指定關(guān)心的描述字的值,該函數(shù)返回時(shí),結(jié)果指示哪些描述字已就緒。該函數(shù)返回后,我們使用FD_ISSET來測試fd_set數(shù)據(jù)類型中的描述字。描述字集中任何與未就緒的描述字對應(yīng)的位返回時(shí)均清為0.為此,每次重新調(diào)用select函數(shù)中,我們都得再次把所有描述字集合中的所關(guān)心的位置為1。這也是在稍候的通信例子里,我們設(shè)置resset和allset兩個(gè)集合的原因所在。
select函數(shù)返回某個(gè)套接口就緒的條件:
select函數(shù)的通信例子:一個(gè)簡單的TCP回射服務(wù)器程序
SelectServer.c: 使用select機(jī)制的服務(wù)器端程序
?
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 描述字的數(shù)量*/
15 int nready ,client[FD_SETSIZE];
16 int n ;
17 /*創(chuàng)建描述字集合,由于select函數(shù)會把未有事件發(fā)生的描述字清零,所以我們設(shè)置兩個(gè)集合*/
18 fd_set rset , allset;
19 char buf[MAXLINE];
20 socklen_t clilen;
21 struct sockaddr_in cliaddr , servaddr;
22 /*創(chuàng)建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 是第一個(gè)描述字*/
33 /*最大的描述字,用于select函數(shù)的第一個(gè)參數(shù)*/
34 maxfd = listenfd;
35 /*client的數(shù)量,用于輪詢*/
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 /*尋找第一個(gè)能放置新的描述字的位置*/
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已經(jīng)滿了*/
62 if(i==FD_SETSIZE)
63 {
64 printf("Too many clients , over stack .\n");
65 return -1;
66 }
67 FD_SET(connfd,&allset);//設(shè)置fd
68 /*更新相關(guān)參數(shù)*/
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 /*當(dāng)對方關(guān)閉的時(shí)候,server關(guān)閉描述字,并將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 }
總結(jié):
在本文中,介紹了select函數(shù)在I/O復(fù)用中相關(guān)內(nèi)容,并給出了一個(gè)典型的TCP反射程序的樣例,使用select可以處理多個(gè)客戶端的并發(fā)需求。在下一篇文章中,將會重點(diǎn)介紹另一種常見的I/O復(fù)用函數(shù)poll的使用。
轉(zhuǎn)載于:https://www.cnblogs.com/coser/archive/2012/02/29/2373478.html
總結(jié)
以上是生活随笔為你收集整理的unix下网络编程之I/O复用(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单问题:sizeof(char型数组)
- 下一篇: .net Csharpt C# UDP