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

UDP套接字编程问题:C代码控制信号接收器阻塞于recvfrom

排查你的UDP通信C代码问题

看起来你已经通过netcat验证了UDP通信是可行的,那我们一步步找出C代码里的问题:

1. UDP的connect误区

首先要明确:UDP是无连接协议,调用connect并不会像TCP那样和设备建立真正的连接,它只是给当前socket绑定了默认的目标地址,让后续的send/recv可以不用重复指定地址。所以你看到的"Connected"提示其实是误导性的——这个connect成功只说明系统能解析目标IP和端口,不代表设备真的"连接"上了。

2. recvfrom的致命参数错误

你的recvfrom调用有两个关键问题:

  • 接收长度参数用了strlen(recepbuff):你用memset(recepbuff, '0', sizeof(recepbuff))把缓冲区填的是ASCII的'0'(不是字符串终止符'\0'),所以strlen(recepbuff)会返回BUFFLEN,这虽然数值上是对的,但正确的做法应该直接用sizeof(recepbuff),因为我们要接收的是字节流,不是依赖字符串长度。
  • 最后一个参数传错了:recvfrom的第6个参数需要是socklen_t*类型的指针,用来返回发送方的地址长度。你直接传了sizeof(serv_addr),这会导致未定义行为,大概率是recvfrom无法正确接收数据,一直阻塞。

修正后的recvfrom调用应该是这样:

socklen_t addr_len = sizeof(serv_addr);
if ((recvfrom(socket_fd, recepbuff, sizeof(recepbuff)-1, 0, (struct sockaddr *)&serv_addr, &addr_len)) < 0)

注意这里用sizeof(recepbuff)-1是为了留一个字节放'\0',方便后续打印字符串。

3. 不安全的gets函数

gets函数已经被废弃了,它不会检查缓冲区大小,很容易导致缓冲区溢出。建议换成fgets,并且要处理掉fgets读取到的换行符(因为你输入命令后按回车,fgets会把\n也读进cmd里,而设备可能只识别不带换行的命令):

printf("Enter command : ");
fgets(cmd, BUFFLEN, stdin);
// 去掉换行符
cmd[strcspn(cmd, "\n")] = '\0';

4. 错误处理不完善

当socket创建失败时,你只打印了错误信息,但程序会继续执行后续代码,这会导致更严重的错误。应该在错误后直接退出,并且用perror代替printf,它能显示系统错误码对应的描述,方便排查问题:

if ( (socket_fd = socket(AF_INET, SOCK_DGRAM, 0) ) < 0 )
{
    perror("ERROR opening socket");
    exit(EXIT_FAILURE);
}

5. 发送数据的小细节

你的sendto调用是正确的,但可以考虑用send代替(因为已经connect过了,默认目标地址就是设备),不过这不是必须的。另外要确保发送的命令格式和netcat一致——比如netcat输入命令后回车,是否会发送换行符?如果设备不需要换行,那用fgets处理掉换行符就很重要。

修正后的完整代码(关键部分修改)

/* Standard Linux headers */
#include <stdio.h> //printf
#include <string.h> //memset
#include <stdlib.h> //exit(0);
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>

/* DEFINE */
#define SERVER "192.168.0.10"
#define BUFFLEN 1024 //Max length of buffer
#define PORT 3000 //The port on which to send data

int main (void)
{
    struct sockaddr_in serv_addr;
    char recepbuff[BUFFLEN];
    char cmd[BUFFLEN];
    int socket_fd;

    memset(recepbuff, 0 ,sizeof(recepbuff)); // 用0初始化,方便后续字符串处理

    if ( (socket_fd = socket(AF_INET, SOCK_DGRAM, 0) ) < 0 )
    {
        perror("ERROR opening socket");
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    if(inet_pton(AF_INET, SERVER, &serv_addr.sin_addr)<=0)
    {
        perror("Invalid address/ Address not supported");
        exit(EXIT_FAILURE);
    }

    // UDP的connect只是绑定默认目标地址,不是真正的连接
    if (connect(socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("Failed to set default target address");
        exit(EXIT_FAILURE);
    }
    printf("Default target address set to %s:%d\n", SERVER, PORT);

    while(1)
    {
        printf("Enter command : ");
        if (!fgets(cmd, BUFFLEN, stdin)) {
            perror("Failed to read command");
            break;
        }
        // 去掉换行符
        cmd[strcspn(cmd, "\n")] = '\0';

        // 用send代替sendto,因为已经connect过了
        if (send(socket_fd, cmd, strlen(cmd), 0) < 0 )
        {
            perror("Failed sending cmd");
        }
        else
        {
            printf("CMD * %s * SENT\n", cmd);
        }

        memset(recepbuff, 0 ,sizeof(recepbuff));
        socklen_t addr_len = sizeof(serv_addr);
        ssize_t recv_len = recvfrom(socket_fd, recepbuff, sizeof(recepbuff)-1, 0, (struct sockaddr *)&serv_addr, &addr_len);
        if (recv_len < 0)
        {
            perror("Failed to receive response");
        }
        else
        {
            recepbuff[recv_len] = '\0'; // 手动添加字符串终止符
            printf("Received response: %s\n", recepbuff);
        }
    }

    close(socket_fd);
    return 0;
}

额外排查建议

  • 确认设备是否会对每个命令返回响应:有些UDP设备可能只在特定命令下返回数据,或者需要特定的命令格式(比如结尾加'\n'或'\r\n')。如果设备不返回响应,recvfrom会一直阻塞,这时候可以给recvfrom设置超时:用setsockopt设置SO_RCVTIMEO选项。
  • 用tcpdump或wireshark抓包,对比netcat发送的数据包和你的C程序发送的数据包,看是否有差异(比如命令内容、端口、IP等)。

内容的提问来源于stack exchange,提问作者HacHac

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:22:00