我們這裡簡單的說下 select 的作用,並給出 select 的客戶端例項。我們知道 select 是io 多路復用的乙個最簡單支援,poll 和 epoll 是 select 的公升級版。我們通常會遇到這樣乙個問題:當客戶端阻塞在 fgets() 等待客戶輸入的時候,伺服器端斷開連線。而客戶端卻不能及時知道,只有在客戶輸入完畢併發送到伺服器的時候才知道連線已經斷開,但是此時可能已經過了很長時間了。如果我們想及時知道伺服器斷開連線怎麼辦呢?
我們知道不管是 fgets() 等待客戶輸入還是 read() 從套介面讀取資料,都是 io 操作。我們不能阻塞在某個 io 操作中的乙個,這樣其他 io 操作會無法進行,即使其他 io 操作上有資料了我們也無法及時讀取。select 的原理是這樣的:我們將這些 io 操作所要操作的檔案描述符放到一起(比如乙個陣列中),然後阻塞在 select() 函式上,為什麼要阻塞在這裡呢?其實這時的 select 實在不停的遍歷這個陣列,檢視其中的檔案描述符上是否可讀/可寫,一旦可讀/可寫,select 返回,停止阻塞。然後我們對可讀/可寫的檔案描述如做相應的操作即可。
int select(nfds, readfds, writefds, exceptfds, timeout)
nfds 是指定 select() 要遍歷的最大檔案描述符 + 1,readfds 就是放檔案描述的陣列,這個陣列裡面關心的是該陣列中檔案描述符的讀事件,wretefds 也是放檔案描述符的陣列,這個陣列裡面關心的是該陣列中檔案描述符的寫事件,exceptfds 也是放檔案描述符的陣列,這個陣列關心的是該陣列中檔案描述符的出錯事件。timeout 是 select 阻塞的時間。如果設定為 空指標,那麼將永遠阻塞下去直到某個描述符有事件發生(就緒)。否則的話就會在阻塞由 timeout 指定的時間後返回,無論關心的檔案描述符是否有事件發生。select 返回有事件發生的檔案描述符個數,失敗返回 -1,超時返回 0!
int fd_isset(int fd, fd_set *set);
這個函式中,fd是set陣列裡的乙個,檢視該fd是否有資料可讀或者可寫(取決於set是讀集合還是寫集合),如果有,則返回非0;
服務端,server.c
#include #include #include #include #include #include "errwrap.h"
#define maxline 80
#define serverport 8000
int main(int argc, char *argv)
}// 達到select能監控的檔案個數上限1024
if (i == fd_setsize)
fd_set(confd, &allset); //將新的新的檔案描述符新增到監控訊號集裡
if (confd > maxfd)
maxfd = confd; //更新最大的檔案描述符
if (i > maxi)
maxi = i; // 更新client最大下標
if (--nret == 0)
continue; /* 如果沒有更多的就緒檔案描述符繼續回到上面select阻塞監聽,負責處理未
處理完的就緒檔案描述符*/
} for (i = 0; i <= maxi; i++)
else
if (--nret == 0)//處理的那個fd不是最後乙個
break;
}}
} close(listenfd);
return 0;
}
服務端 client.c
#include #include #include #include #include "errwrap.h"
#define maxline 80
#define serv_port 8000
int main(int argc, char *argv)
struct sockaddr_in servaddr;
char buf[maxline];
int sockfd, n;
sockfd = socket(af_inet, sock_stream, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = af_inet;
inet_pton(af_inet, argv[1], &servaddr.sin_addr);
servaddr.sin_port = htons(serv_port);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while (fgets(buf, maxline, stdin) != null)
close(sockfd);
return 0;
}
中間檔案 errwrap.h
#ifndef __errwrap__h
#define __errwrap__h
void perr_exit(const char *s);
void err_exit(const char *s);
int accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void bind(int fd, const struct sockaddr *sa, socklen_t salen);
void connect(int fd, const struct sockaddr *sa, socklen_t salen);
void listen(int fd, int backlog);
int socket(int family, int type, int protocol);
ssize_t read(int fd, void *ptr, size_t nbytes);
ssize_t write(int fd, const void *ptr, size_t nbytes);
void close(int fd);
ssize_t readn(int fd, void *vptr, size_t n);
ssize_t writen(int fd, const void *vptr, size_t n);
#endif
中間檔案 errwrap.c
#include #include #include #include //錯誤處理與退出
void perr_exit(const char *s)
void err_exit(const char *s)
//發生連線錯誤或者被訊號中斷後,將再次進入accept。
int accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
return n;
}void bind(int fd, const struct sockaddr *sa, socklen_t salen)
void connect(int fd, const struct sockaddr *sa, socklen_t salen)
void listen(int fd, int backlog)
int socket(int family, int type, int protocol)
//沒有讀取到資料,失敗原因是因為訊號中斷,則重新讀取
ssize_t read(int fd, void *ptr, size_t nbytes)
return n;
}ssize_t write(int fd, const void *ptr, size_t nbytes)
return n;
}void close(int fd)
ssize_t readn(int fd, void *vptr, size_t n)
else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
} return n - nleft;
}ssize_t writen(int fd, const void *vptr, size_t n)
nleft -= nwritten;
ptr += nwritten;
} return n;
}
客戶端編譯:gcc client.c errwrap.c -o client
服務端編譯: gcc server.c errwrap.c -o server
先執行服務端: ./server
再執行客戶端: ./client 127.0.0.1
在客戶端輸入:duanjinhui
返回 duanjinhui
服務端會顯示:received from 127.0.0.1 at port 56641
可以再開幾個終端執行client程式,可以看到服務端的併發現象
Linux系統程式設計 IO多路復用之select
i o 多路復用技術是為了解決程序或執行緒阻塞到某個 i o 系統呼叫而出現的技術,使程序不阻塞於某個特定的 i o 系統呼叫。select poll epoll 都是i o多路復用的機制。i o多路復用通過一種機制,可以監視多個描述符,一旦某個描述符就緒 一般是讀就緒或者寫就緒,就是這個檔案描述符...
linux網路程式設計中INADDR ANY的含義
inaddr any 選項網路程式設計中常用到bind函式,需要繫結ip位址,這時可以設定inaddr any inaddr any就是指定位址為0.0.0.0的位址,這個位址事實上表示不確定位址,或 所有位址 任意位址 也就是表示本機的所有ip,因為有些機子不止一塊網絡卡,多網絡卡的情況下,這個就...
linux網路程式設計中INADDR ANY的含義
inaddr any選項 網路程式設計中常用到bind函式,需要繫結ip位址,這時可以設定inaddr any inaddr any就是指定位址為0.0.0.0的位址,這個位址事實上表示不確定位址,或 所有位址 任意位址 也就是表示本機的所有ip,因為有些機子不止一塊網絡卡,多網絡卡的情況下,這個就...