Linux 網路程式設計中的 select 函式

2021-07-30 06:22:31 字數 4508 閱讀 6980

我們這裡簡單的說下 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,因為有些機子不止一塊網絡卡,多網絡卡的情況下,這個就...