C语言网络编程广播场景多接收端回复时发送端接收问题咨询
问题解答
结论
你的理解完全正确:当前sender.c仅调用一次recvfrom(),确实只会读取最先到达的1条确认消息,后续到达的确认消息如果没被读取,会在你调用close()关闭socket后直接被系统丢弃,相当于全部丢失。
UDP是无连接协议,所有接收端的响应报文都会按到达顺序存入socket的内核接收缓冲区,单次recvfrom()只会读取缓冲区最前面的1个报文,剩下的报文会留在缓冲区等待读取,直到缓冲区满了之后新的报文才会被直接丢弃。
修改方案
你只需要对发送端做两处改动即可收集所有接收端的确认:
- 给socket设置接收超时,避免无限等待:因为你无法提前预知局域网内有多少接收端,所以设置一个合理的超时时间(比如3秒),超时后就认为所有响应都已经返回。
- 循环调用
recvfrom(),直到触发超时错误为止,每次收到响应后打印发送方的IP和确认内容即可。
修改后完整sender.c代码
#include <stdio.h> #include <netinet/in.h> #include <string.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h> #define PORT 4242 // 接收超时时间,单位秒 #define RECV_TIMEOUT 3 int main() { int socket_fd; struct sockaddr_in receiver, sender; char send_buff[256], receive_buff[256]; socklen_t sender_size; socket_fd = socket(AF_INET, SOCK_DGRAM, 0); if (socket_fd < 0) { perror("failed to create socket"); return -1; } int broadcast_yes = 1; if (setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, (void*) &broadcast_yes, sizeof(broadcast_yes)) < 0) { perror("failed to set broadcast socket option"); return -2; } // 新增:设置接收超时 struct timeval timeout = {RECV_TIMEOUT, 0}; if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { perror("failed to set receive timeout"); return -2; } memset(&receiver, 0, sizeof(receiver)); receiver.sin_family = AF_INET; receiver.sin_port = htons(PORT); // 注意替换为你自己局域网的广播地址 receiver.sin_addr.s_addr = inet_addr("192.168.1.255"); strcpy(send_buff, "i'm sending a broadcast message"); if (sendto(socket_fd, (void*)send_buff, strlen(send_buff) + 1, 0, (struct sockaddr*) &receiver, sizeof(receiver)) < 0) { perror("failed to send broadcast message"); return -3; } printf("已发送广播,等待%ds接收响应...\n", RECV_TIMEOUT); // 循环接收所有响应 while(1) { memset(receive_buff, 0, sizeof(receive_buff)); sender_size = sizeof(sender); int ret = recvfrom(socket_fd, (void*)receive_buff, sizeof(receive_buff), 0, (struct sockaddr*) &sender, &sender_size); if (ret < 0) { // 超时错误,说明没有更多响应了 if (errno == EAGAIN || errno == EWOULDBLOCK) { printf("接收超时,所有响应接收完成\n"); break; } perror("failed to receive confirmation message"); return -4; } // 打印发送方IP和响应内容 printf("收到来自%s的响应:%s\n", inet_ntoa(sender.sin_addr), receive_buff); } close(socket_fd); return 0; }
额外优化建议
- 如果接收端可能重复发送响应,你可以用一个数组或者哈希表存储已经收到过的IP地址,遇到重复的IP直接跳过即可。
- 注意广播地址要和你的局域网网段匹配,比如如果你的虚拟机网段是192.168.122.0/24,广播地址应该改为192.168.122.255,否则广播消息无法发送到正确的局域网。
内容的提问来源于stack exchange,提问作者abc xyz
相关产品推荐
相关产品推荐

