日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux--select

發布時間:2024/9/30 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux--select 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在linux中的I/O都是用文件描述符表示的,文件描述符就是一個int,比如0是標準輸入,1是標準輸出,2是標準錯誤輸出。同理,socket也是返回一個int文件描述符。

select系統調用是用來讓我們的程序監視多個文件描述符(file descriptor)的狀態變化的。程序會停在select這里等待,直到被監視的文件描述符有某一個或多個發生了狀態改變。

只要有一個描述符狀態變為可用,select會停止堵塞,繼續往下執行,這時我們就需要用輪詢的方法查出是哪個文件描述符可用。

理解select模型的關鍵在于理解fd_set,為說明方便,取fd_set長度為1字節,fd_set中的每一bit可以對應一個文件描述符fd。則1字節長的fd_set最大可以對應8個fd。
(1)執行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。
(2)若fd=5,執行FD_SET(fd,&set);后set變為00000100(第5位置為1)
(3)若再加入fd=2,fd=1,則set變為01100100
(4)執行select(6,&set,0,0,0)阻塞等待(第一個參數是最大的文件描述符+1)
(5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變為01100000。注意:沒有事件發生的fd=5被清空。

注意第五條中的fd=5被清空,說明只要是不活躍的文件描述符都會在select的阻塞被結束后從原有的文件描述集合fd_set中被去除。因此在socket使用select的場景下,是可以在while輪詢中使用FD_ISSET宏來不斷監聽服務器端生成的socket文件描述符是否活躍。


FD_ZERO(fd_set *fdset):清空fdset與所有文件句柄的聯系。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd與fdset的聯系。
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd與fdset的聯系。
FD_ISSET(int fd, fdset *fdset):檢查fdset聯系的文件句柄fd是否可讀寫,>0表示可讀寫。


可以采用select實現 ??并發服務器:多路復用I/O

下面是服務器select并發版本:

http://blog.csdn.net/vagrxie/article/details/3948204

select模型屬于網絡的I/O復用模型,比純粹的阻塞I/O模型更具有實用性,因為可以同時等待多個描述字的就緒。

當年學習C/C++的時候,很少碰到底層以數字標示的描述字,只在寫文件系統的去嘗試各種情況,以獲得最佳效率的時候實際嘗試使用過一次,一直覺得那種open,write,read的文件操作方式,實在是比fopen一族函數還要低級的方式-_-!平時沒有必要使用。但是等到網絡編程的時候,才發現。。。。原來這么底層的東西,竟然也有一定的通用性,文件的描述字和網絡的描述字竟然是一致的-_-!不管是誰設計的,還是挺佩服的。。。。。。

???????這里僅僅是為了學習Select模型而寫的學習例子,作用是在服務器端輸出連接上的客戶端的IP(僅以數字形式),然后將客戶端的IP以字符串的形式返回,客戶端連接服務器,并接受由服務器端返回的IP地址,然后輸出轉換為字符串形式的IP地址和數字形式的IP地址,為了區別select到正確的不同listen套接字,這里用了不同的端口,并且不同的兩個套接字響應時以echo 1,echo 2區別。功能是很簡單的,僅僅用于學習,所以其中很多地方本來可以抽出來稱為函數的,都貪簡單,直接復制了(-_-!這里本來習慣想說Ctrl-C Ctrl-V的。。。但是發現自己實在Ubuntu下用vim復制的,好像和實際情況不符。。。。)

???????另外。。。。由于用的是《Unix Network Programming》一書,所以編程風格都變得有點像書中了。。。。服務器端全是自己寫的,客戶端代碼由書中的daytime客戶端改過來的,并且發現書中客戶端代碼都不關閉套接字,都交由退出進程的時候由系統關閉,不知道這種風格好不好。由于學習。。。寫的是ANSI C程序,用gcc編譯-_-!

unp.h是《Unix Network Programming》源代碼中的公用頭文件,makefile可能也得注意一下,為了圖省事,我用了其源代碼中的Make.defines,因為這樣比自己寫簡單多了:),makefile就不貼了,沒有什么學習意義。

?

運行效果如下:

客戶端運行:

./TestSelectCli 127.0.0.1 1000

Conncet OK

127.0.0.1:16777343 Echo 1.

laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 127.0.0.1 1001

Conncet OK

127.0.0.1:16777343 Echo 2.

laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 192.168.0.138 1000

Conncet OK

192.168.0.138:2315299008 Echo 1.

laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 192.168.0.138 1001

Conncet OK

192.168.0.138:2315299008 Echo 2.

?

服務器端輸出:

2315299008 Echo 1.

16777343 Echo 1.

16777343 Echo 2.

2315299008 Echo 1.

2315299008 Echo 2.

?

?

服務器端源代碼:

?

??1?#include????"unp.h"
??2?
??3?
??4?void?str_echo1(int?connfd);
??5?void?str_echo2(int?connfd);
??6?
??7?int?main(int?argc,?char?**argv)
??8?{
??9?????struct?sockaddr_in cliaddr;
?10?????pid_t childpid;
?11?
?12?????/*??Bind 1000 port to listen socket 1 */
?13?????int?listenfd1 = Socket(AF_INET, SOCK_STREAM,?0);
?14?
?15?????struct?sockaddr_in servaddr;
?16?????bzero(&servaddr,?sizeof(servaddr));
?17?????servaddr.sin_family = AF_INET;
?18?????servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
?19?????servaddr.sin_port = htons(1000);
?20?
?21?????Bind(listenfd1, (SA *)&servaddr,?sizeof(servaddr));
?22?
?23?????Listen(listenfd1, LISTENQ);
?24?
?25?????/*??Bind 1001 port to listen socket 2*/
?26?????int?listenfd2 = Socket(AF_INET, SOCK_STREAM,?0);
?27?
?28?????struct?sockaddr_in servaddr2;
?29?????bzero(&servaddr2,?sizeof(servaddr2));
?30?????servaddr2.sin_family = AF_INET;
?31?????servaddr2.sin_addr.s_addr = htonl(INADDR_ANY);
?32?????servaddr2.sin_port = htons(1001);
?33?
?34?????Bind(listenfd2, (SA *)&servaddr2,?sizeof(servaddr2));
?35?
?36?????Listen(listenfd2, LISTENQ);
?37?
?38?????/*?Initialize fd_set struct */
?39?????int?maxfdp1 = max(listenfd1, listenfd2) +?1;
?40?????fd_set rset;
?41?????FD_ZERO(&rset);
?42?
?43?????/*??Select from this two listen socket */
?44?????for( ; ; )
?45?????{
?46?????????FD_SET(listenfd1, &rset);
?47?????????FD_SET(listenfd2, &rset);
?48?
?49?????????int?nready = -1;
?50?????????if( (nready = select(maxfdp1, &rset,?NULL,?NULL,NULL)) <?0)//只要有一個客戶連接,select就停止堵塞
?51?????????{
?52?????????????if(EINTR?== errno)
?53?????????????{
?54?????????????????continue;
?55?????????????}
?56?????????????else
?57?????????????{
?58?????????????????err_sys("Select error.");
?59?????????????}
?60?????????}
?61?
?62?????????/*??some one listening socket is readable.*/
?63?????????if(FD_ISSET(listenfd1, &rset))
?64?????????{
?65?????????????socklen_t len =?sizeof(cliaddr);
?66?????????????int?connfd = Accept(listenfd1, (SA *)&cliaddr, &len);
?67?
?68?????????????if(?0?== (childpid = Fork()) )
?69?????????????{
?70?????????????????/*?child process */
?71?????????????????Close(listenfd1);
?72?
?73?????????????????str_echo1(connfd);
?74?????????????????exit(0);
?75?????????????}
?76?
?77?????????????/*?parent process??*/
?78?????????????Close(connfd);
?79?
?80?????????}
?81?
?82?
?83?????????if(FD_ISSET(listenfd2, &rset))
?84?????????{
?85?????????????socklen_t len =?sizeof(cliaddr);
?86?????????????int?connfd = Accept(listenfd2, (SA *)&cliaddr, &len);
?87?
?88?????????????if(?0?== (childpid = Fork()) )
?89?????????????{
?90?????????????????/*?child process */
?91?????????????????Close(listenfd2);
?92?
?93?????????????????str_echo2(connfd);
?94?????????????????exit(0);
?95?????????????}
?96?
?97?????????????/*?parent process??*/
?98?????????????Close(connfd);
?99?
100?????????}
101?
102?????}
103?
104?????exit(0);
105?}
106?
107?void?str_echo1(int?connfd)
108?{
109?????struct?sockaddr_in clientAddr;
110?????socklen_t len =?sizeof(clientAddr);
111?
112?????if(getpeername(connfd, (SA*) &clientAddr, &len) <?0)
113?????{
114?????????return;
115?????}
116?
117?????char?lcBuffer[MAXLINE] = {0};
118?????sprintf(lcBuffer,?"%u?Echo 1.", clientAddr.sin_addr.s_addr);
119?
120?????printf("%s/n", lcBuffer);
121?
122?????Write(connfd, lcBuffer, MAXLINE);
123?}
124?
125?
126?void?str_echo2(int?connfd)
127?{
128?????struct?sockaddr_in clientAddr;
129?????socklen_t len =?sizeof(clientAddr);
130?
131?????if(getpeername(connfd, (SA*) &clientAddr, &len) <?0)
132?????{
133?????????return;
134?????}
135?
136?
137?????char?lcBuffer[MAXLINE] = {0};
138?????sprintf(lcBuffer,?"%u?Echo 2.", clientAddr.sin_addr.s_addr);
139?
140?????printf("%s/n", lcBuffer);
141?
142?????Write(connfd, lcBuffer, MAXLINE);
143?}
144?
145

?

客戶端源代碼:

?1?#include????"unp.h"
?2?
?3?int?main(int?argc,?char?**argv)
?4?{
?5?????int????????????????????sockfd, n;
?6?????char?????????????? recvline[MAXLINE +?1];
?7?????struct?sockaddr_in servaddr;
?8?
?9?????if?(argc !=?3)
10?????????err_quit("usage: a.out <IPaddress> <IPPort>");
11?
12?????int?port = atoi(argv[2]);
13?
14?????if?( (sockfd = socket(AF_INET, SOCK_STREAM,?0)) <?0)
15?????????err_sys("socket error");
16?
17?????bzero(&servaddr,?sizeof(servaddr));
18?????servaddr.sin_family = AF_INET;
19?????servaddr.sin_port?? = htons(port);
20?????if?(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <=0)
21?????????err_quit("inet_pton error for?%s", argv[1]);
22?
23?????if?(connect(sockfd, (SA *) &servaddr,?sizeof(servaddr)) <?0)
24?????????err_sys("connect error");
25?
26?????printf("Conncet OK/n");
27?
28?????while?( (n = read(sockfd, recvline, MAXLINE)) >?0) {
29?????????recvline[n] =?0;??/*?null terminate */
30?
31?????????/*??change number string to number and to ip string */
32?????????struct?in_addr svraddr;
33?????????svraddr.s_addr = strtoul(recvline,?NULL,?10);
34?????????char?*pszsvraddr = inet_ntoa(svraddr);
35?
36?????????printf("%s:%s/n", pszsvraddr, recvline);
37?????}
38?????if?(n <?0)
39?????????err_sys("read error");
40?
41?????exit(0);
42?}


下面更簡潔的代碼會加深理解

我們要監控readfd文件描述符數組

int use_select(int *readfd,int n)?
{?
?? fd_set my_readfd;?
?? int maxfd;?
?? int i;?
????
?? maxfd=readfd[0];?
?? for(i=1;i <n;++i) ? if(readfd[i]>maxfd) maxfd=readfd[i];?
?? while(1)?
?? {?
??????? /*?? 將所有的文件描述符加入?? */?
??????? FD_ZERO(&my_readfd);?
??????? for(i=0;i??<n;++i)?????????? FD_SET(readfd[i],*my_readfd);?
??????? /*???? 進程阻塞???????????????? */?
??????? select(maxfd+1,& my_readfd,NULL,NULL,NULL);?
??????? /*??????? 有東西可以讀了?????? */?
??????? for(i=0;i?<n;++i)? ? ?if(FD_ISSET(readfd[i],&my_readfd))?
????????????? {?
????????????????? /* 原來是我可以讀了 */?
??????????????????????? we_read(readfd[i]);?
????????????? }?
?? }?
}


select監控的是一個文件描述符數組,一旦至少一個文件描述符可以用了,select停止堵塞,這時程序需要一個輪詢確定具體哪個文件可以用了。


下面是一個?用select實現的異步聊天程序:

服務器端:

#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/types.h>#define MAXBUF 1024 /************關于本文檔******************************************** *filename: async-server.c *purpose: 演示網絡異步通訊,這是服務器端程序 *wrote by: zhoulifa(zhoulifa@163.com) 周立發(http://zhoulifa.bokee.com) Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言 *date time:2007-01-25 21:22 *Note: 任何人可以任意復制代碼并運用這些文檔,當然包括你的商業用途 * 但請遵循GPL *Thanks to: Google.com *Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力 * 科技站在巨人的肩膀上進步更快!感謝有開源前輩的貢獻! *********************************************************************/int main(int argc, char **argv) {int sockfd, new_fd;socklen_t len;struct sockaddr_in my_addr, their_addr;unsigned int myport, lisnum;char buf[MAXBUF + 1];fd_set rfds;struct timeval tv;int retval, maxfd = -1;if (argv[1])myport = atoi(argv[1]);elsemyport = 7838;if (argv[2])lisnum = atoi(argv[2]);elselisnum = 2;if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {perror("socket");exit(1);}bzero(&my_addr, sizeof(my_addr));my_addr.sin_family = PF_INET;my_addr.sin_port = htons(myport);if (argv[3])my_addr.sin_addr.s_addr = inet_addr(argv[3]);elsemy_addr.sin_addr.s_addr = INADDR_ANY;if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) {perror("bind");exit(1);}if (listen(sockfd, lisnum) == -1) {perror("listen");exit(1);}while (1) {printf("\n----等待新的連接到來開始新一輪聊天……\n");len = sizeof(struct sockaddr);if ((new_fd =accept(sockfd, (struct sockaddr *) &their_addr,&len)) == -1) {perror("accept");exit(errno);} elseprintf("server: got connection from %s, port %d, socket %d\n",inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd);/* 開始處理每個新連接上的數據收發 */printf("\n準備就緒,可以開始聊天了……直接輸入消息回車即可發信息給對方\n");while (1) {/* 把集合清空 */FD_ZERO(&rfds);/* 把標準輸入句柄0加入到集合中 */FD_SET(0, &rfds);maxfd = 0;/* 把當前連接句柄new_fd加入到集合中 */FD_SET(new_fd, &rfds);if (new_fd > maxfd)maxfd = new_fd;/* 設置最大等待時間 */tv.tv_sec = 1;tv.tv_usec = 0;/* 開始等待 */retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);if (retval == -1) {printf("將退出,select出錯! %s", strerror(errno));break;} else if (retval == 0) {/* printf("沒有任何消息到來,用戶也沒有按鍵,繼續等待……\n"); */continue;} else {if (FD_ISSET(0, &rfds)) {/* 用戶按鍵了,則讀取用戶輸入的內容發送出去 */bzero(buf, MAXBUF + 1);fgets(buf, MAXBUF, stdin);if (!strncasecmp(buf, "quit", 4)) {printf("自己請求終止聊天!\n");break;}len = send(new_fd, buf, strlen(buf) - 1, 0);if (len > 0)printf("消息:%s\t發送成功,共發送了%d個字節!\n",buf, len);else {printf("消息'%s'發送失敗!錯誤代碼是%d,錯誤信息是'%s'\n",buf, errno, strerror(errno));break;}}if (FD_ISSET(new_fd, &rfds)) {/* 當前連接的socket上有消息到來則接收對方發過來的消息并顯示 */bzero(buf, MAXBUF + 1);/* 接收客戶端的消息 */len = recv(new_fd, buf, MAXBUF, 0);if (len > 0)printf("接收消息成功:'%s',共%d個字節的數據\n",buf, len);else {if (len < 0)printf("消息接收失敗!錯誤代碼是%d,錯誤信息是'%s'\n",errno, strerror(errno));elseprintf("對方退出了,聊天終止\n");break;}}}}close(new_fd);/* 處理每個新連接上的數據收發結束 */printf("還要和其它連接聊天嗎?(no->退出)");fflush(stdout);bzero(buf, MAXBUF + 1);fgets(buf, MAXBUF, stdin);if (!strncasecmp(buf, "no", 2)) {printf("終止聊天!\n");break;}}close(sockfd);return 0; }
用select實現服務器的好處是,當客戶與其建立連接之后,客戶可能要過段時間才發送消息,如果之前普通的處理,服務器只能堵塞在recv,不能有其他操作;而用select監控輸入文件描述符和socket描述符,只要有一個準備好,select就返回,譬如,select堵塞時,只要服務器輸入消息,并回車發送時,select檢測到輸入文件描述符,就立馬返回,將消息發送給客戶端;



客戶端程序:

#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h>#define MAXBUF 1024 /************關于本文檔******************************************** // *filename: ssync-client.c *purpose: 演示網絡異步通訊,這是客戶端程序 *wrote by: zhoulifa(zhoulifa@163.com) 周立發(http://zhoulifa.bokee.com) Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言 *date time:2007-01-25 21:32 *Note: 任何人可以任意復制代碼并運用這些文檔,當然包括你的商業用途 * 但請遵循GPL *Thanks to: Google.com *Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力 * 科技站在巨人的肩膀上進步更快!感謝有開源前輩的貢獻! *********************************************************************/ int main(int argc, char **argv) {int sockfd, len;struct sockaddr_in dest;char buffer[MAXBUF + 1];fd_set rfds;struct timeval tv;int retval, maxfd = -1;if (argc != 3) {printf("參數格式錯誤!正確用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用來從某個 IP 地址的服務器某個端口接收最多 MAXBUF 個字節的消息",argv[0], argv[0]);exit(0);}/* 創建一個 socket 用于 tcp 通信 */if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("Socket");exit(errno);}/* 初始化服務器端(對方)的地址和端口信息 */bzero(&dest, sizeof(dest));dest.sin_family = AF_INET;dest.sin_port = htons(atoi(argv[2]));if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {perror(argv[1]);exit(errno);}/* 連接服務器 */if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {perror("Connect ");exit(errno);}printf("\n準備就緒,可以開始聊天了……直接輸入消息回車即可發信息給對方\n");while (1) {/* 把集合清空 */FD_ZERO(&rfds);/* 把標準輸入句柄0加入到集合中 */FD_SET(0, &rfds);maxfd = 0;/* 把當前連接句柄sockfd加入到集合中 */FD_SET(sockfd, &rfds);if (sockfd > maxfd)maxfd = sockfd;/* 設置最大等待時間 */tv.tv_sec = 1;tv.tv_usec = 0;/* 開始等待 */retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);if (retval == -1) {printf("將退出,select出錯! %s", strerror(errno));break;} else if (retval == 0) {/* printf("沒有任何消息到來,用戶也沒有按鍵,繼續等待……\n"); */continue;} else {if (FD_ISSET(sockfd, &rfds)) {/* 連接的socket上有消息到來則接收對方發過來的消息并顯示 */bzero(buffer, MAXBUF + 1);/* 接收對方發過來的消息,最多接收 MAXBUF 個字節 */len = recv(sockfd, buffer, MAXBUF, 0);if (len > 0)printf("接收消息成功:'%s',共%d個字節的數據\n",buffer, len);else {if (len < 0)printf("消息接收失敗!錯誤代碼是%d,錯誤信息是'%s'\n",errno, strerror(errno));elseprintf("對方退出了,聊天終止!\n");break;}}if (FD_ISSET(0, &rfds)) {/* 用戶按鍵了,則讀取用戶輸入的內容發送出去 */bzero(buffer, MAXBUF + 1);fgets(buffer, MAXBUF, stdin);if (!strncasecmp(buffer, "quit", 4)) {printf("自己請求終止聊天!\n");break;}/* 發消息給服務器 */len = send(sockfd, buffer, strlen(buffer) - 1, 0);if (len < 0) {printf("消息'%s'發送失敗!錯誤代碼是%d,錯誤信息是'%s'\n",buffer, errno, strerror(errno));break;} elseprintf("消息:%s\t發送成功,共發送了%d個字節!\n",buffer, len);}}}/* 關閉連接 */close(sockfd);return 0; } 編譯用如下命令:
gcc -Wall async-server.c -o server
gcc -Wall async-client.c -o client
運行用如下命令:
./server 7838 1
./client 127.0.0.1 7838



下面還是一個簡單的并發處理的服務器,但是僅僅是能多路復用I/O,但是不能同時處理連接請求,因為這只有一個線程

http://www.cnblogs.com/faraway/archive/2009/03/06/1404449.html

使用select函數可以以非阻塞的方式和多個socket通信。程序只是演示select函數的使用,功能非常簡單,即使某個連接關閉以后也不會修改當前連接數,連接數達到最大值后會終止程序。

1. 程序使用了一個數組fd_A,通信開始后把需要通信的多個socket描述符都放入此數組。

2. 首先生成一個叫sock_fd的socket描述符,用于監聽端口。

3. 將sock_fd和數組fd_A中不為0的描述符放入select將檢查的集合fdsr。

4. 處理fdsr中可以接收數據的連接。如果是sock_fd,表明有新連接加入,將新加入連接的socket描述符放置到fd_A。

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#define MYPORT 1234 // the port users will be connecting to#define BACKLOG 5 // how many pending connections queue will hold#define BUF_SIZE 200int fd_A[BACKLOG]; // accepted connection fdint conn_amount; // current connection amountvoid showclient(){int i;printf("client amount: %d\n", conn_amount);for (i = 0; i < BACKLOG; i++) {printf("[%d]:%d ", i, fd_A[i]);}printf("\n\n");}int main(void){int sock_fd, new_fd; // listen on sock_fd, new connection on new_fdstruct sockaddr_in server_addr; // server address informationstruct sockaddr_in client_addr; // connector's address informationsocklen_t sin_size;int yes = 1;char buf[BUF_SIZE];int ret;int i;if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {perror("socket");exit(1);}if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {perror("setsockopt");exit(1);}server_addr.sin_family = AF_INET; // host byte orderserver_addr.sin_port = htons(MYPORT); // short, network byte orderserver_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IPmemset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind");exit(1);}if (listen(sock_fd, BACKLOG) == -1) {perror("listen");exit(1);}printf("listen port %d\n", MYPORT);fd_set fdsr;int maxsock;struct timeval tv;conn_amount = 0;sin_size = sizeof(client_addr);maxsock = sock_fd;while (1) {// initialize file descriptor setFD_ZERO(&fdsr);FD_SET(sock_fd, &fdsr);// timeout settingtv.tv_sec = 30;tv.tv_usec = 0;// add active connection to fd setfor (i = 0; i < BACKLOG; i++) {if (fd_A[i] != 0) {FD_SET(fd_A[i], &fdsr);}}ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);if (ret < 0) {perror("select");break;} else if (ret == 0) {printf("timeout\n");continue;}// check every fd in the setfor (i = 0; i < conn_amount; i++) {if (FD_ISSET(fd_A[i], &fdsr)) {ret = recv(fd_A[i], buf, sizeof(buf), 0);if (ret <= 0) { // client closeprintf("client[%d] close\n", i);close(fd_A[i]);FD_CLR(fd_A[i], &fdsr);fd_A[i] = 0;} else { // receive dataif (ret < BUF_SIZE)memset(&buf[ret], '\0', 1);printf("client[%d] send:%s\n", i, buf);}}}// check whether a new connection comesif (FD_ISSET(sock_fd, &fdsr)) {new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);if (new_fd <= 0) {perror("accept");continue;}// add to fd queueif (conn_amount < BACKLOG) {fd_A[conn_amount++] = new_fd;printf("new connection client[%d] %s:%d\n", conn_amount,inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));if (new_fd > maxsock)maxsock = new_fd;}else {printf("max connections arrive, exit\n");send(new_fd, "bye", 4, 0);close(new_fd);break;}}showclient();}// close other connectionsfor (i = 0; i < BACKLOG; i++) {if (fd_A[i] != 0) {close(fd_A[i]);}}exit(0);}
有網友提出了點改進:

accept函數里面fd_A[conn_amount++] = new_fd;可以稍加改進,按照樓主的意圖,會出現當一個用戶不斷連接再斷開的情況下,當連接次數超過maxconnection的時候,就會退出,因此fd_A[i]沒有很好的利用,不能實現動態管理,我建議僅將conn_amount僅作為客戶端連接數,而不是有連接就增加,當accept成功的時候,就加1,當recv=0的時候就減1;建議將fd_A[conn_amount++] = new_fd;這句程序改為
for(i = 0;i < MAXCLIENT;i++)
{
if(fd[i] == 0)
{
fd[i] = new_fd;
break;
}

}
conn_amount++;
這樣就可以重復利用fd[i]的空間;
另外在recv返回值<=0的時候,加一句conn_amount++;
還有一點,超過最大連接數的時候break應該為continue,這樣會更人性化一點,客戶端太多關閉它的請求就行了,沒必要自毀,這樣整個系統就可以動態與客戶端實現連接,





總結

以上是生活随笔為你收集整理的linux--select的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。