socket 使用select 非阻塞方式實現

2021-08-09 20:59:37 字數 4402 閱讀 6084

select函式原型如下:

int select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

select系統呼叫是用來讓我們的程式監視多個檔案控制代碼(socket 控制代碼)的狀態變化的。程式會停在select這裡等待,直到被監視的檔案控制代碼有乙個或多個發生了狀態改變

有一片博文寫得非常詳細易理解推薦大家看看,這裡就不說了。

主要貼**,參考的也是別人的**,但是發現有bug,努力修正後實現多台客戶段與一台伺服器通訊:在非阻塞模式下,伺服器和客戶端可以自由發訊息,不必等待回答,目前伺服器發的訊息,所有客戶端都會收到此訊息。讀者可以自己改一下,讓伺服器與指定的客戶端通訊(可以通過鍵盤輸入要通訊的客戶端編號來控制,或者用棧或佇列來儲存客戶端編號,伺服器在分別傳送訊息):

伺服器端**:

#include

#include

#include

#include

#include

#include

#include

#define backlog 5

//已經連線上的套接字個數

#define concurrent_max 8

//應用層同時可以處理的連線

#define server_port 11332

#define buffer_size 1024

#define quit_cmd ".quit"

int client_fds[concurrent_max]

;int

main

(int argc,

const

char

* argv)

; struct sockaddr_in;

* */

//本地位址

struct sockaddr_in server_addr;

server_addr.sin_family = af_inet;

server_addr.sin_port =

htons

(server_port)

; server_addr.sin_addr.s_addr =

inet_addr

("127.0.0.1");

bzero(&

(server_addr.sin_zero),8

);/* * int socket(int family,int type,int protocol)

family:

指定使用的協議簇:af_inet(ipv4) af_inet6(ipv6) af_local(unix協議) af_route(路由套接字) af_key(秘鑰套接字)

type:

指定使用的套接字的型別:sock_stream(位元組流套接字) sock_dgram

protocol:

如果套接字型別不是原始套接字,那麼這個引數就為0

* */

//建立socket

int server_sock_fd =

socket

(af_inet, sock_stream,0)

;if(server_sock_fd ==-1

)/** int bind(int sockfd, struct sockaddr *myaddr, int addrlen)

sockfd:

socket函式返回的套接字描述符

myaddr:

是指向本地ip位址的結構體指標

myaddrlen:

結構體長度

* */

//繫結socket

int bind_result =

bind

(server_sock_fd,

(struct sockaddr *

)&server_addr,

sizeof

(server_addr));

if(bind_result ==-1

)/** int listen(int sockfd, int backlog)

sockfd:

socket函式繫結bind後套接字描述符

backlog:

設定可連線客戶端的最大連線個數,當有多個客戶端向伺服器請求時,收到此值的影響。預設值20

* */

//listen if(

listen

(server_sock_fd, backlog)==-

1)//fd_set

fd_set server_fd_set;

int max_fd =-1

;struct timeval tv;

//超時時間設定

while(1

)//printf("stdin_fileno=%d\n", stdin_fileno);

//伺服器端socket

fd_set

(server_sock_fd,

&server_fd_set)

;// printf("server_sock_fd=%d\n", server_sock_fd);

if(max_fd < server_sock_fd)

//客戶端連線

for(

int i =

0; i < concurrent_max; i++)}

}int ret =

select

(max_fd +1,

&server_fd_set,

null

,null

,&tv);if

(ret <0)

else

if(ret ==0)

else

for(

int i =

0; i < concurrent_max; i++)}

}if(fd_isset

(server_sock_fd,

&server_fd_set))}

if(index >=0)

else}}

for(

int i =

0; i < concurrent_max; i++)

recv_msg[byte_num]

='\0'

;printf

("客戶端(%d):%s\n"

, i, recv_msg);}

else

if(byte_num <0)

else}}

}}}return0;

}

客戶端**:

#include

#include

#include

#include

#include

#include

#include

#define buffer_size 1024

intmain

(int argc,

const

char

* argv)

char recv_msg[buffer_size]

;char input_msg[buffer_size];/*

* int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen)

sockfd:

socket函式返回套接字描述符

serv_addr:

伺服器ip位址結構指標

addrlen:

結構體指標的長度

* */if(

connect

(server_sock_fd,

(struct sockaddr *

)&server_addr,

sizeof

(struct sockaddr_in))==

0)}if

(fd_isset

(server_sock_fd,

&client_fd_set))

recv_msg[byte_num]

='\0'

;printf

("伺服器:%s\n"

, recv_msg);}

else

if(byte_num <0)

else}}

//}

}return0;

}

除錯時發現,select函式每次呼叫後,如果超時,都會把struct timeval tv 設定為0,這樣再次呼叫select時它會立即返回,根本不會監視socket控制代碼,導致乙個超時的死迴圈。

所以每次呼叫select之後都要重新給struct timeval tv 賦值。

非阻塞式socket程式設計select

華清遠見嵌入式學院 上海中心講師。select在socket程式設計中還是比較重要的,可是對於初學socket的人來說都不太愛用select寫程式,他們只是習慣寫諸如 connect accept recv或recvfrom這樣的阻塞程式 所謂阻塞方式block,顧名思義,就是程序或是執行緒執行到這...

非阻塞式socket程式設計(select )

select在socket程式設計中還是比較重要的,可是對於初學socket的人來說都不太愛用select寫程式,他們只是習慣寫諸如connect accept recv或recvfrom這樣的阻塞程式 所謂阻塞方式block,顧名思義,就是程序或是執行緒執行到這些函式時必須等待某個事件的發生,如果...

非阻塞式socket程式設計select

select在socket程式設計中還是比較重要的,可是對於初學socket的人來說都不太愛用select寫程式,他們只是習慣寫諸如connect accept recv或recvfrom這樣的阻塞程式 所謂阻塞方式block,顧名思義,就是程序或是執行緒執行到這些函式時必須等待某個事件的發生,如果...