You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:28:10