[英]Why does select() function always return 0 in my UDP server implementation?
我正在嘗試實現一個單播UDP服務器,該服務器在多個客戶端請求服務時為其提供服務。 正在發送的消息是更新的計數器值。 我希望服務器能夠在有一個請求且沒有請求的情況下接收傳入的請求,繼續將數據一個接一個地發送到客戶端列表。 我嘗試使用select()
實現此功能,但它始終返回0。我在做什么錯?
服務器端-實現select()
:
while(1)
{
// >>> Step #3 <<<
// Wait to receive a message from client
sleep(10); // Unix sleep for 1 second
printf(".\n");
printf("Waiting for recvfrom() to complete... \n");
FD_ZERO(&readhandle);
FD_SET(server_s1, &readhandle);
FD_SET(server_s2, &readhandle);
timeout_interval.tv_sec = 10;
timeout_interval.tv_usec = 500000;
int retval = select(max_servers+1, &readhandle, NULL, NULL, &timeout_interval);
if (retval == -1)
{
printf("Select error\n");
}
else if (retval == 0)
{
printf("timeout\n");
}
else
{
if (FD_ISSET(server_s1, &readhandle))
{
addr_len = sizeof(client_addr);
errno = 0;
retcode = recvfrom(server_s1, in_buf, sizeof(in_buf), 0, (struct sockaddr *)&client_addr, &addr_len);
if (retcode > 0)
{
// Copy the four-byte client IP address into an IP address structure
memcpy(&client_ip_addr, &client_addr.sin_addr.s_addr, 4);
// Print an informational message of IP address and port of the client
printf("IP address of client = %s port = %d) \n", inet_ntoa(client_ip_addr),ntohs(client_addr.sin_port));
// Output the received message
printf("Received from client: %s \n", in_buf);
client_port = ntohs(client_addr.sin_port);
insert_at_end(client_port, client_addr);
printf("Client added :\n");
display();
}
// >>> Step #4 <<<
// Send to the client using the server socket
sprintf(out_buf, "Sending update from SERVER to CLIENT %d",counter++);
struct node *tmp;
tmp=head;
while(tmp!=NULL)
{
retcode = sendto(server_s1, out_buf, (strlen(out_buf) + 1), 0,(struct sockaddr *)&(tmp -> client_addr), sizeof(tmp -> client_addr));
printf("IP address of client = %s port = %d) \n", inet_ntoa(tmp -> client_addr.sin_addr),ntohs(tmp -> port_num));
if (retcode < 0)
{
printf("*** ERROR - sendto() failed \n");
exit(-1);
}
tmp=tmp->next;
}
}
if(FD_ISSET(server_s2, &readhandle))
{
addr_len = sizeof(client_addr);
errno = 0;
retcode = recvfrom(server_s2, in_buf, sizeof(in_buf), 0, (struct sockaddr *)&client_addr, &addr_len);
if (retcode > 0)
{
// Copy the four-byte client IP address into an IP address structure
memcpy(&client_ip_addr, &client_addr.sin_addr.s_addr, 4);
// Print an informational message of IP address and port of the client
printf("IP address of client = %s port = %d) \n", inet_ntoa(client_ip_addr),ntohs(client_addr.sin_port));
// Output the received message
printf("Received acknowledgement from the client: %s \n", in_buf);
client_port = ntohs(client_addr.sin_port);
retcode = sendto(server_s2, out_buf, (strlen(out_buf) + 1), 0,(struct sockaddr *)&(client_addr), sizeof(client_addr));
if (retcode < 0)
{
printf("*** ERROR - sendto() failed \n");
exit(-1);
}
}
}
}
}
對於第一個參數select()
是nfds
,FDS的數量 ......不是最后的FD的數量-你可能想server_s + 1
,在這里。
為了完整性起見稍后添加-收集其他注釋等並在相同的地方擴展...
... select()的其他參數已寫入(或可能寫入)-因此您需要在每次調用之前進行設置。 因此:
正如@JeremyFriesner指出的那樣,您需要先重新創建所有fd_set
然后再將其傳遞給select()
,因為select()
返回時,只有可讀取或可寫入(或有異常)的fd才會出現在它們中。各自的fd_set
。
實現此目的的明顯方法是為您當前正在等待的所有事物提供一個單獨的fd_set
,並在將其傳遞給select()
之前將其復制到“工作”版本。 當您開始使用“寫就緒”時,您會發現通常將設置“讀就緒”一次並保留它(除非入站緩沖區已滿),但是只有在具備“寫就緒”狀態時,才設置“寫就緒”有待寫的東西,一旦清空出站緩沖區,它將清除它。
正如@rici所指出的,可能需要刷新超時。
POSIX在這個問題上非常出色。 它確實說:
•成功完成后,select()函數可以修改timeout參數指向的對象。
但我注意到它沒有說的東西包括:
select()
如何修改超時。
錯誤發生了什么...特別是EINTR
(!)
pselect()
可能不會修改超時-盡管從它采用const struct timespec*
的事實可以很明顯地看出來。
無論如何, pselect()
更好地標准化了,信號掩碼的處理值得一讀-在您發現沒有它的日子里。
一種可能性是您沒有在每次調用select
之前重新初始化tv
。 某些操作系統(包括Linux)會更新該參數的值,以指示剩余的等待時間。 最佳做法是在每次調用之前重新初始化超時值。
在Linux的select(2)
聯機幫助頁中:
在Linux上,select()修改超時以反映未休眠的時間; 大多數其他實現則不這樣做。 (POSIX.1-2001允許任何一種行為。)這將導致以下問題:將讀取超時的Linux代碼移植到其他操作系統,以及將代碼移植到在一個循環中為多個select()重用struct timeval的Linux移植代碼時,都會導致問題。重新初始化它。 在select()返回之后,將超時視為未定義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.