You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

非阻塞UDP套接字调用recvfrom始终返回EWOULDBLOCK无法接收消息的问题求助

非阻塞UDP套接字调用recvfrom始终返回EWOULDBLOCK无法接收消息的问题求助

看起来你遇到的问题主要是错误处理逻辑的漏洞,再加上一些代码细节的小问题,共同导致了非阻塞模式下recvfrom一直返回EWOULDBLOCK的假象。让我一步步帮你分析和修复:

核心问题分析

你的代码最关键的错误是错误处理逻辑完全颠倒了:你现在是调用recvfrom后直接检查errno == EWOULDBLOCK,但正确的逻辑应该是先判断recvfrom的返回值是否为-1,再去检查errno的值。

原因很简单:如果recvfrom成功接收到数据,返回值是正数(接收到的字节数),此时errno的值是未定义的(可能保留了之前的错误状态,或者是随机值),这会导致你误判以为又出现了EWOULDBLOCK错误。

除此之外还有两个次要问题:

  1. 复用了同一个addr结构体,既用来设置服务器绑定地址,又用来存储客户端地址,容易造成逻辑混淆;
  2. 每次调用recvfrom前没有重置客户端地址长度变量,若之前的调用修改了这个值,会导致后续recvfrom行为异常。

修复后的服务器代码

// 修正后的 server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUFSIZE 1024
#define PORT 12345

int main(int argc, char** argv)
{
    char buffer[BUFSIZE];
    int rv;
    int sock;
    // 分开服务器地址和客户端地址,避免复用混淆
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);


    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    // 添加socket创建的错误检查
    sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
    if (sock == -1) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 添加bind的错误检查
    if (bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind failed");
        close(sock);
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        memset(buffer, 0, sizeof(buffer));
        // 每次调用recvfrom前,重置客户端地址长度为正确值
        client_addr_len = sizeof(client_addr);
        rv = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_addr_len);
        
        if (rv == -1) {
            // 只有recvfrom返回-1时,才需要检查errno
            if (errno == EWOULDBLOCK || errno == EAGAIN) {
                // 无数据待接收,属于正常情况
                puts(".");
            } else {
                // 真正的系统错误,打印并退出
                perror("recvfrom error");
                close(sock);
                exit(EXIT_FAILURE);
            }
        } else {
            // 成功接收到客户端消息,处理并打印
            char ipstr[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, sizeof(ipstr));
            buffer[rv] = '\0';
            // 注意:客户端端口是网络字节序,要用ntohs转为主机字节序
            printf("new request from [%s:%d]:\n%s\n", ipstr, ntohs(client_addr.sin_port), buffer);
        }
      
        sleep(1);
    }

    close(sock);
    return 0;
}

客户端代码的可选优化

你的客户端代码基本没问题,这里只是添加了系统调用的错误检查,让代码更健壮:

// 优化后的 client.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUFSIZE 1024
#define PORT 12345
#define IPADDR "127.0.0.1"

const char* msgtosend = "can you hear me?";

int main(int argc, char** argv)
{
    char buffer[BUFSIZE];
    int sock;
    struct sockaddr_in addr;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    if (inet_pton(AF_INET, IPADDR, &addr.sin_addr.s_addr) <= 0) {
        perror("inet_pton error");
        exit(EXIT_FAILURE);
    }

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // UDP的connect只是设置默认发送地址,失败概率低但仍建议检查
    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("connect error");
        close(sock);
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        ssize_t send_rv = send(sock, msgtosend, strlen(msgtosend), 0);
        if (send_rv == -1) {
            perror("send error");
            close(sock);
            exit(EXIT_FAILURE);
        }
        puts(".");
        sleep(1);
    }

    close(sock);
    return 0;
}

修复效果说明

现在运行修改后的服务器和客户端,你应该能正常接收到客户端发送的消息了。核心的修复点是:

  1. 修正了recvfrom的错误处理逻辑,只有当返回-1时才检查errno
  2. 每次调用recvfrom前重置客户端地址长度,确保参数正确;
  3. 拆分了服务器地址和客户端地址的结构体,避免逻辑混淆;
  4. 添加了系统调用的错误检查,方便快速定位异常。

备注:内容来源于stack exchange,提问作者ugo_capeto

火山引擎 最新活动