我正在尝试实现一个单播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);
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
第一个参数select()
是fds nfds
的数量 ...而不是最后一个 fd 的数量- 你可能想要的server_s + 1
,这里.
稍后添加完整性 - 收集其他评论等并扩展相同...
... select()的其他参数是(或可能)写入 - 所以你需要在每次调用之前设置它们.因此:
正如@JeremyFriesner所指出的那样,你需要fd_set
在传递之前重新创建所有的select()
- 因为当select()
返回时,只有fd的是读取就绪或写入就绪(或有异常)将出现在它们各自的中fd_set
.
实现这一目标的显而易见的方法是为fd_set
您当前正在等待的所有事物分别设置,并在传递之前将其复制到"工作"版本select()
.当你开始使用'write-ready'时,你会发现一般情况下你会设置'read-ready'一次并离开它(除非你的入站缓冲区填满),但你只有当你拥有时才设置'write-ready'待写的东西,一旦你清空了出站缓冲区就会清除它.
正如@rici指出的那样,超时可能需要刷新.
POSIX非常倾向于此事.它确实说:
•成功完成后,select()函数可能会修改timeout参数指向的对象.
但我注意到它没有说的内容包括:
如何select()
修改超时.
错误会发生什么...特别是EINTR
(!)
这pselect()
可能不会修改超时 - 尽管这很明显,因为它需要一个const struct timespec*
.
无论如何,pselect()
更好的标准化,信号掩码的处理是值得理解的 - 相对于你发现你不能没有它的那一天.