本地主机C语言多播问题:参与者无法接收服务器多播消息
多播消息接收问题排查请求
我将参与者与领导者的代码放在同一个send.c文件中,通过代码结构区分角色。目前领导者可成功发送消息,服务器也已在多播组内广播消息(Wireshark已验证),但参与者无法接收来自服务器的Multicast消息,请求协助排查send.c中参与者端的消息接收问题。
相关代码
主函数代码
int main(int argc, char **argv) { ssize_t n; int sockfd; int recv_s; struct user_account currentUser; struct sockaddr_in servaddr,localSock; struct ip_mreq mreq; socklen_t len = sizeof(servaddr); int roleChoice; char sendline[MAX_BUFF], recvline[MAX_BUFF]; u_int yes=1; // user struct instance memset(¤tUser, 0, sizeof(currentUser)); sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("Błąd w tworzeniu gniazda"); exit(EXIT_FAILURE); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) { perror("Błąd w inet_pton"); exit(EXIT_FAILURE); } // mcast socket recv_s = socket(AF_INET, SOCK_DGRAM, 0); if (recv_s < 0) { perror("Błąd w tworzeniu gniazda odbiorczego"); exit(EXIT_FAILURE); } setsockopt(recv_s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); fcntl(recv_s, F_SETFL, O_NONBLOCK); #ifdef SO_REUSEPORT if (setsockopt(recv_s, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) { perror("setsockopt(SO_REUSEPORT) failed"); exit(EXIT_FAILURE); } #endif // preparing to receive memset(&localSock, 0, sizeof(localSock)); localSock.sin_family = AF_INET; localSock.sin_port = htons(MULTICAST_PORT); localSock.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(recv_s, (struct sockaddr*)&localSock, sizeof(localSock)) < 0) { perror("Błąd w bind gniazda odbiorczego multicastu"); exit(EXIT_FAILURE); } //role if(currentUser.is_instructor){ //joining the session //creating a session session_active = 1; //** if (session_active) { join_multicast_group(sockfd, MULTICAST_GROUP); } while (session_active) { char message[256]; printf("msg to send (lub 'quit' aby wyjść): "); fgets(message, 256, stdin); message[strcspn(message, "\n")] = 0; if (strncmp(message, "quit", 4) == 0) { break; // Wyjście z pętli } sprintf(sendline, "MULTICAST_MSG %d: %s", currentUser.training_id, message); sendto(sockfd, sendline, strlen(sendline), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr)); printf("Wysłano wiadomość: %s\n", message); } leave_multicast_group(sockfd, MULTICAST_GROUP); }
服务器端代码
else if(strncmp(buffer, "MULTICAST_MSG", 13) == 0) { int session_id; char message_content[MAX_BUFF]; if (sscanf(buffer, "MULTICAST_MSG %d: %[^\t\n]", &session_id, message_content) == 2) { send_multicast_message(send_s, message_content, MULTICAST_GROUP, MULTICAST_PORT); printf("Rozesłano wiadomość multicastową do uczestników sesji %d.\n", session_id); } else { printf("Niepoprawny format wiadomości multicastowej.\n"); }
void send_multicast_message(int send_socket, const char* message_content, const char* multicast_group, int multicast_port) { struct sockaddr_in multicast_addr; memset(&multicast_addr, 0, sizeof(multicast_addr)); multicast_addr.sin_family = AF_INET; multicast_addr.sin_addr.s_addr = inet_addr(multicast_group); multicast_addr.sin_port = htons(multicast_port); if (sendto(send_socket, message_content, strlen(message_content), 0, (struct sockaddr*)&multicast_addr, sizeof(multicast_addr)) < 0) { perror("sendto failed"); } else { printf("Rozesłano wiadomość multicastową.\n"); } }
send.c中的参与者代码
//joining the session join_multicast_group(recv_s, MULTICAST_GROUP); printf("waiting for mcast messages...\n"); int session_active = 1; while (session_active) { char multicast_message[MAX_BUFF]; memset(multicast_message, 0, MAX_BUFF); socklen_t addrlen = sizeof(localSock); ssize_t multicast_len = recvfrom(recv_s, multicast_message, MAX_BUFF, 0, (struct sockaddr *)&localSock, &addrlen); if (multicast_len > 0) { multicast_message[multicast_len] = '\0'; printf("mcast msg: %s\n", multicast_message); } usleep(100000); // Sleep for 100 milliseconds } leave_multicast_group(recv_s, MULTICAST_GROUP);
额外补充的send_multicast_message代码
void send_multicast_message(int send_socket, const char* message_content, const char* multicast_group, int multicast_port) { struct sockaddr_in multicast_addr; memset(&multicast_addr, 0, sizeof(multicast_addr)); multicast_addr.sin_family = AF_INET; multicast_addr.sin_addr.s_addr = inet_addr(multicast_group); multicast_addr.sin_port = htons(multicast_port); sendto(send_socket, message_content, strlen(message_content), 0, (struct sockaddr*)&multicast_addr, sizeof(multicast_addr)) < 0) }
运行日志
./send (presenter) enter message('quit' to exit) : hi! send: hi! ./recv received: MULTICAST_MSG 10: hi! sending multicast. send mcast message to participants session no 10. ./send (participant) u joined the session waiting for mcast messages...
排查建议
- 检查
join_multicast_group实现:确认参与者端调用的join_multicast_group函数是否正确设置了IP_ADD_MEMBERSHIP选项,绑定的socket是否为参与者的recv_s,且多播组地址参数正确。 - 处理非阻塞socket错误:参与者的
recv_s为非阻塞模式,当前代码未处理recvfrom返回的EAGAIN/EWOULDBLOCK以外的错误,可添加判断排查潜在问题:if (multicast_len < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { perror("recvfrom failed"); session_active = 0; } } - 确认多播路由与网卡配置:检查参与者主机的多播路由设置,确保多播数据包能到达正确网卡;可在
ip_mreq中指定imr_interface为对应网卡IP,强制绑定特定网卡加入多播组。 - 验证端口与地址一致性:再次确认参与者绑定的
MULTICAST_PORT、加入的MULTICAST_GROUP与服务器发送的完全一致,包括宏定义值的正确性。 - 排查防火墙规则:临时关闭参与者主机的防火墙,测试是否能接收多播消息,确认是否存在端口拦截。
内容的提问来源于stack exchange,提问作者user7777777
相关产品推荐
相关产品推荐

