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,顧名思義,就是程序或是執行緒執行到這些函式時必須等待某個事件的發生,如果...