Linux為多路復用IO提供了較多的接口,有select(),pselect(),poll()的方式,繼承自BSD和System V 兩大派系。
select模型比較簡單,“輪詢”檢測fd_set的狀態,然後再采取相應的措施。
信號驅動模型有必要仔細研究一下,一般有如下步驟:
具體進下文client例程。
寫了一個聊天程序的demo,把這兩種技術都使用了。服務端采取多路復用的IO方式,代替多進(線)程的模型,客服端采取的是信號驅動,如下:
容易產生bug的地方都寫注釋裡邊了。
serv.c
1 #include <sys/select.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <arpa/inet.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <signal.h>
11
12 void endServ(int sig)
13 {
14 printf("Server ended!\n");
15 exit(-1);
16 }
17
18 int main()
19 {
20 // signal
21 struct sigaction act;
22 sigemptyset(&act.sa_mask);
23 act.sa_handler = endServ;
24 act.sa_flags = 0;
25 sigaction(SIGINT,&act,0);
26 printf("This server is started,enter CTRL+C to end. \n");
27
28 int servfd = socket(AF_INET,SOCK_STREAM,0);
29 if(servfd == -1) {
30 printf("something wrong with socket():%m\n");
31 exit(-1);
32 }
33
34 struct sockaddr_in addr;
35 addr.sin_family = AF_INET;
36 addr.sin_port = htons(9999);
37 inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
38
39 if (bind(servfd,(struct sockaddr*)&addr,sizeof(addr)) == -1) {
40 printf("Some thing wrong with bind():%m\n");
41 exit(-1);
42 }
43 printf("Bind success!\n");
44
45 listen(servfd,20);
46
47 int numbers=0;//how many clients has accepted
48 fd_set fs;
49 FD_ZERO(&fs);
50 int client_fd[100];
51 int i;
52 for(i=0;i<100;++i)
53 {
54 client_fd[i] = -1;
55 }
56
57 int maxfd=0;
58 char buf[1024];
59 for(;;)
60 {
61 maxfd =0;
62 FD_ZERO(&fs);
63 FD_SET(servfd,&fs);
64 maxfd = maxfd>servfd?maxfd:servfd;
65 for(i=0;i<numbers;++i)
66 {
67 if(client_fd[i] != -1) {
68 FD_SET(client_fd[i],&fs);
69 maxfd = maxfd>client_fd[i]?maxfd:client_fd[i];
70 }
71 }
72
73 int res = select(maxfd+1,&fs,0,0,0);
74 if(res == -1) {
75 printf("Something wrong with select():%m\n");
76 exit(-1);
77 }
78
79 if(FD_ISSET(servfd,&fs) && numbers < 100) {
80 printf("New client!\n");
81 client_fd[numbers] = accept(servfd,0,0);
82 numbers++;
83 }
84
85 for(i=0;i<numbers;++i)
86 {
87 bzero(buf,sizeof(buf));
88 //judge if client_fd[i] equal -1 is necessary
89 //if a client quited,next time the program will
90 //have a segment default
91 //also it should be in the front.
92 if(client_fd[i] != -1 && FD_ISSET(client_fd[i],&fs))
93 {
94 res = recv(client_fd[i],buf,sizeof(buf),0);
95 if(res == 0) {
96 printf("A client quit\n");
97 close(client_fd[i]);
98 FD_CLR(client_fd[i],&fs);
99 client_fd[i] = -1;
100 }
101 else if(res == -1) {
102 printf("Something wrong with net.\n");
103 exit(-1);
104 }
105 else {
106 int j;
107 for(j=0;j<numbers;++j)
108 {
109 if(client_fd[j] != -1)
110 send(client_fd[j],buf,sizeof(buf),0);
111 }
112 }
113 }
114 }
115 }
116 }
client:
1 #include <signal.h>
2 #include <unistd.h>
3 #include <sys/socket.h>
4 #include <fcntl.h>
5 #include <arpa/inet.h>
6 #include <netinet/in.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <errno.h>
11
12 static int fd;
13
14 void show(int sig)
15 {
16 char buf[1024] = {0};
17 int n = read(fd,buf,sizeof(buf));
18 buf[n] = 0;
19 write(1,"MSG:",strlen("MSG:"));
20 write(1,buf,strlen(buf));
21 }
22
23 int main()
24 {
25 struct sigaction act;
26 sigemptyset(&act.sa_mask);
27 act.sa_handler = show;
28 //This is necessary,in last loop read() counld be interrupt;
29 act.sa_flags = SA_RESTART;
30 sigaction(SIGIO,&act,0);
31
32 fd = socket(AF_INET,SOCK_STREAM,0);
33 if(fd == -1) {
34 printf("Cannot get socketfd!:%m\n");
35 exit(-1);
36 }
37
38 struct sockaddr_in addr;
39 addr.sin_family = AF_INET;
40 addr.sin_port = htons(9999);
41 inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
42
43 if(connect(fd,(struct sockaddr*)&addr,sizeof(addr)) != -1)
44 printf("connect success!\n");
45 else
46 exit(-1);
47
48 int flag = fcntl(fd,F_GETFL);
49 flag |= O_ASYNC;
50 fcntl(fd,F_SETFL,flag);
51 fcntl(fd,F_SETOWN,getpid());
52
53 char buffer[1024]={0};
54 for(;;)
55 {
56 int n = read(0,buffer,sizeof(buffer));
57 if(n==-1)
58 break;
59 send(fd,buffer,n,0);
60 }
61
62 write(1,"Closed.",strlen("Closed."));
63 }