AF_LOCAL UDP Socket客户端无法接收数据的排查与解决请求
AF_LOCAL UDP Socket 服务端客户端通信故障排查与修复
问题描述
在Ubuntu系统上编写AF_LOCAL(Unix域)UDP Socket的服务端与客户端代码,服务端持续向套接字发送整数数据,输出符合预期,但客户端始终无法接收数据,持续输出No data available。
服务端代码(server.c)
//server.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #define SOCKET_PATH "/tmp/my_socket" int main() { int server_fd; struct sockaddr_un server_addr; int value = 0; // Initial integer value to send // Remove existing socket file if present unlink(SOCKET_PATH); // Create socket if ((server_fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } // Set up server address memset(&server_addr, 0, sizeof(struct sockaddr_un)); server_addr.sun_family = AF_LOCAL; strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1); // Bind socket if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_un)) == -1) { perror("bind"); exit(EXIT_FAILURE); } printf("Server started. Writing integer values to socket continuously...\n"); // Continuously write integer values to socket while (1) { // Print the integer value printf("Sending value to client: %d\n", value); // Convert integer value to string char buffer[32]; snprintf(buffer, sizeof(buffer), "%d", value); // Send integer value to client if (sendto(server_fd, buffer, strlen(buffer), 0, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_un)) == -1) { perror("sendto"); } // Increment value for the next iteration value++; // Sleep for a short duration (e.g., 1 second) sleep(1); } // Close server socket (not reachable in this example) close(server_fd); return 0; }
服务端运行输出:
Server started. Writing integer values to socket continuously... Sending value to client: 0 Sending value to client: 1 Sending value to client: 2 ...
客户端代码(client.c)
//client.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #include <fcntl.h> #define SOCKET_PATH "/tmp/my_socket" int main() { int client_fd; struct sockaddr_un server_addr; char buffer[1024]; // Create socket if ((client_fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } // Set socket to non-blocking mode if (fcntl(client_fd, F_SETFL, O_NONBLOCK) == -1) { perror("fcntl"); exit(EXIT_FAILURE); } // Set up server address memset(&server_addr, 0, sizeof(struct sockaddr_un)); server_addr.sun_family = AF_LOCAL; strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1); printf("Client connected to server. Reading integer values...\n"); // Continuously read integer values from server while (1) { // Receive integer value from server ssize_t bytes_received = recv(client_fd, buffer, sizeof(buffer), 0); if (bytes_received == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // No data available, continue loop printf("No data available\n"); continue; } else { perror("recv"); exit(EXIT_FAILURE); } } else if (bytes_received == 0) { printf("Server closed the connection\n"); break; } // Null-terminate the received data buffer[bytes_received] = '\0'; // Print received data printf("Received data from server: %s\n", buffer); } // Close client socket close(client_fd); return 0; }
客户端运行输出:
Client connected to server. Reading integer values... No data available No data available No data available ...
问题根源与修复方案
1. 服务端sendto目标地址错误
UDP是无连接协议,服务端调用sendto时必须指定客户端的地址,而非自身绑定的地址。当前服务端将自身绑定地址作为目标,相当于把数据发送给自己,客户端自然无法接收。
修复后的服务端代码
//server.c(修复版) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #include <string.h> #define SOCKET_PATH "/tmp/my_socket" int main() { int server_fd; struct sockaddr_un server_addr, client_addr; socklen_t client_addr_len = sizeof(client_addr); int value = 0; char init_buffer[32]; unlink(SOCKET_PATH); if ((server_fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sun_family = AF_LOCAL; strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1); if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(EXIT_FAILURE); } printf("Server started. Waiting for client initial message...\n"); // 先接收客户端的初始消息,获取客户端地址 ssize_t recv_len = recvfrom(server_fd, init_buffer, sizeof(init_buffer), 0, (struct sockaddr*)&client_addr, &client_addr_len); if (recv_len == -1) { perror("recvfrom"); exit(EXIT_FAILURE); } printf("Got client connection, starting to send data...\n"); while (1) { printf("Sending value to client: %d\n", value); char buffer[32]; snprintf(buffer, sizeof(buffer), "%d", value); // 向客户端地址发送数据 if (sendto(server_fd, buffer, strlen(buffer), 0, (struct sockaddr*)&client_addr, client_addr_len) == -1) { perror("sendto"); } value++; sleep(1); } close(server_fd); return 0; }
2. 客户端未向服务端发送初始数据,接收方式错误
UDP客户端需要先发送一个初始数据给服务端,让服务端获取自身地址;同时客户端应使用recvfrom(而非recv)接收数据,避免地址信息丢失。
修复后的客户端代码
//client.c(修复版) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #include <fcntl.h> #include <string.h> #define SOCKET_PATH "/tmp/my_socket" int main() { int client_fd; struct sockaddr_un server_addr; char buffer[1024]; socklen_t server_addr_len = sizeof(server_addr); if ((client_fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } // 可选:去掉非阻塞模式,避免频繁打印无数据提示;若保留需增加sleep降低循环频率 // if (fcntl(client_fd, F_SETFL, O_NONBLOCK) == -1) { // perror("fcntl"); // exit(EXIT_FAILURE); // } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sun_family = AF_LOCAL; strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1); // 发送初始消息给服务端,让服务端获取客户端地址 const char* init_msg = "Hello server"; if (sendto(client_fd, init_msg, strlen(init_msg), 0, (struct sockaddr*)&server_addr, server_addr_len) == -1) { perror("sendto"); exit(EXIT_FAILURE); } printf("Client connected to server. Reading integer values...\n"); while (1) { // 使用recvfrom接收数据 ssize_t bytes_received = recvfrom(client_fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_addr_len); if (bytes_received == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { printf("No data available\n"); sleep(1); // 增加sleep减少循环频率 continue; } else { perror("recvfrom"); exit(EXIT_FAILURE); } } else if (bytes_received == 0) { printf("Server closed the connection\n"); break; } buffer[bytes_received] = '\0'; printf("Received data from server: %s\n", buffer); } close(client_fd); return 0; }
调试方法
- 检查套接字文件:执行
ls -l /tmp/my_socket确认服务端绑定后套接字文件存在。 - 手动测试收发:使用
socat - UNIX-CONNECT:/tmp/my_socket直接连接套接字,测试数据是否能正常收发。 - 打印错误信息:确保代码中
perror能正确输出系统错误,排查地址传递、套接字操作的异常。 - 验证地址信息:在服务端
recvfrom后打印客户端地址的sun_path,确认获取到正确的客户端套接字信息。
内容的提问来源于stack exchange,提问作者jtr
相关产品推荐
相关产品推荐

