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

C语言Daytime协议UDP扩展:sendto()报‘非套接字操作’求助

问题诊断与修复方案

核心错误原因

  1. UDP与TCP的socket逻辑混淆:
    • TCP中accept()返回新的客户端socket描述符,用于后续通信;但UDP是无连接协议,recvfrom()返回的是接收的字节数,不是socket描述符。你把这个字节数赋值给fd,然后传给sendto(),自然会触发"非socket上的操作"错误。
  2. UDP的sendto参数错误:
    • sendto()需要明确知道目标客户端的地址,你在sending_datetime函数内新建了空的client结构体,没有传入实际获取到的客户端地址,同时用了错误的fd(字节数)。
  3. UDP不必要的socket关闭:
    • UDP全程复用服务器的监听socket,不需要像TCP那样关闭客户端socket,你原来的逻辑中错误地尝试关闭一个非socket的fd。

具体修改步骤

1. 调整主函数的UDP处理逻辑

  • 把存储recvfrom返回值的变量从fd改为recv_len(避免与socket描述符混淆);
  • 在recvfrom成功接收数据后,直接调用发送函数,使用服务器的监听socket(cfg->sockfd),并传入获取到的客户端地址;
  • 移除UDP模式下的close(fd)操作。

修改后主函数的UDP分支:

else if (cfg->proto == UDP)
{
    char buffer[MSGLENGTH];
    ssize_t recv_len; // 存储接收的字节数,而非socket描述符

    if ((recv_len = recvfrom(cfg->sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client, &client_len)) == -1)
    {
        if (errno != EWOULDBLOCK && errno != EAGAIN)
            ERROR("Error receiving message");
    }
    else
    {
        buffer[recv_len] = '\0';
        printf("Received message from %s:%d: %s\n", 
        inet_ntoa(client.sin_addr), ntohs(client.sin_port), buffer);

        LOG("Successful incoming datagram from %s:%d.\n", \
            inet_ntoa(client.sin_addr),ntohs(client.sin_port));

        // 调用修改后的sending_datetime,传入服务器socket、客户端地址
        if (sending_datetime(cfg->sockfd, UDP, &client, client_len) == -1)
            ERROR("Error sending datetime via UDP");

        LOG("Successfully sent datetime to %s:%d.\n", \
            inet_ntoa(client.sin_addr),ntohs(client.sin_port));
    }
}

同时,主函数中原有的if (fd != -1)分支仅保留TCP模式的处理,调整为:

// 仅TCP模式处理该分支
if (cfg->proto == TCP && fd != -1) {
    LOG("Successful incoming connection from %s:%d.\n", \
        inet_ntoa(client.sin_addr),ntohs(client.sin_port));

    if (sending_datetime(fd, TCP, NULL, 0) == -1)
        ERROR("Error sending datetime");

    LOG("Successfully sent datetime to %s:%d.\n", \
        inet_ntoa(client.sin_addr),ntohs(client.sin_port));

    if (close(fd) == -1)
        ERROR("Error on closing socket");

    LOG("Successfully closing connection from %s:%d.\n", \
        inet_ntoa(client.sin_addr),ntohs(client.sin_port));
}

2. 修改sending_datetime函数

  • 添加客户端地址和长度参数,用于UDP模式的sendto()调用;
  • 移除函数内部空的client结构体定义,使用传入的客户端地址。

修改后的函数声明:

int sending_datetime(int fd, int port, struct sockaddr_in *client_addr, socklen_t client_len);

修改后的函数实现:

int sending_datetime(int fd, int port, struct sockaddr_in *client_addr, socklen_t client_len)
{
    time_t curr_time;
    char buffer[MSGLENGTH];

    curr_time = time(NULL);
    snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&curr_time));

    if (port == TCP)
    {
        return write(fd, buffer, strlen(buffer));
    }
    else if (port == UDP)
    {  
        ssize_t n;
        n = sendto(fd, buffer, strlen(buffer), 0, (struct sockaddr *)client_addr, client_len);
        if (n < 0)
        {
            ERROR("ERROR IN SENDTO()");
        }    
        return n;
    }
    return -1; // 默认返回错误
}

额外注意事项

  • 确保client_len在UDP模式下始终是socklen_t类型(原代码中是int,虽然多数系统兼容,但标准要求用socklen_t);
  • 非阻塞socket的处理逻辑保持不变,注意EWOULDBLOCK/EAGAIN的判断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 04:12:21