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

水下ROV双Raspberry Pi UDP跨设备通信故障排查求助

水下ROV UDP跨设备通信故障排查请求

我正在开发一款水下ROV,水面控制单元与ROV各搭载一台Raspberry Pi,二者通过100米光纤传输控制信号、环境数据及视频。选用UDP传输简单ASCII消息,同一Pi上运行客户端与服务端代码通信正常,但部署到不同设备后完全无法通信:

  • 试过两台设备分别连接不同DHCP路由器(网段10.0.10.151-155与10.0.10.161-165)
  • 试过连接同一路由器
    均无通信。已关闭WiFi(原VNC测试网段10.0.0.xxx),使用以太网口通信。当前UDP程序绑定了固定inet_addr地址,每台设备仅用一个端口,怀疑是绑定地址的问题,但没找到明确的双端口使用示例。

以下是使用的UDP服务端与客户端代码:

UDP服务端代码

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>  
#include <arpa/inet.h>

int main(){
    int udpSocket, nBytes;
    char buffer[1024];
    struct sockaddr_in serverAddr, clientAddr;
    struct sockaddr_storage serverStorage;
    socklen_t addr_size, client_addr_size;
    int i;

    /*Create UDP socket*/
    udpSocket = socket(PF_INET, SOCK_DGRAM, 0);

    /*Configure settings in address struct*/
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(7891);
    serverAddr.sin_addr.s_addr = inet_addr("10.0.10.170");
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  

    /*Bind socket with address struct*/
    bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));

    /*Initialize size variable to be used later on*/
    addr_size = sizeof serverStorage;

    while(1){
        /* Try to receive any incoming UDP datagram. Address and port of 
        requesting client will be stored on serverStorage variable */
        nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&serverStorage, &addr_size);

         /*Send message back to client, using serverStorage as the address*/
        sendto(udpSocket,buffer,nBytes,0,(struct sockaddr *)&serverStorage,addr_size);
        }

    return 0;
}

UDP客户端代码

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>    
#include <arpa/inet.h>

int main(){
    int clientSocket, portNum, nBytes;
    char buffer[1024];
    struct sockaddr_in serverAddr;
    socklen_t addr_size;

    /*Create UDP socket*/
    clientSocket = socket(PF_INET, SOCK_DGRAM, 0);

    /*Configure settings in address struct*/
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(7891);
    serverAddr.sin_addr.s_addr = inet_addr("10.0.10.172");
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  

     /*Initialize size variable to be used later on*/
     addr_size = sizeof serverAddr;

    while(1){
        printf("Type a sentence to send to server:\n");
        fgets(buffer,1024,stdin);
        printf("You typed: %s",buffer);

        nBytes = strlen(buffer) + 1;
    
        /*Send message to server*/
        sendto(clientSocket,buffer,nBytes,0,(struct sockaddr *)&serverAddr,addr_size);

        /*Receive message from server*/
        nBytes = recvfrom(clientSocket,buffer,1024,0,NULL, NULL);

        printf("Received from server: %s\n",buffer);

        }

        return 0;
}

核心问题分析与修复步骤

1. 绑定地址错误(最可能原因)

服务端代码绑定了固定IP 10.0.10.170,这意味着socket只会监听这个特定IP的数据包。如果Raspberry Pi的实际以太网IP不是这个值,数据包根本无法被接收。正确做法是绑定到通配地址INADDR_ANY,让socket监听所有可用网络接口的数据包:

// 替换服务端中的绑定地址行
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

2. 客户端目标IP配置错误

客户端代码中设置的目标IP是10.0.10.172,需确认该IP确实是服务端设备的以太网实际IP。可在服务端Pi上执行ifconfig eth0或ip a show eth0查看当前IP。

3. 网络连通性验证(先于代码调试)

修改代码前,先确认两台设备的网络可达性:

  • 在客户端Pi上ping服务端IP:ping 10.0.10.xxx(替换为服务端实际IP)
  • 使用nc工具测试UDP连通性:
    • 服务端运行:nc -ul 7891
    • 客户端运行:nc -u <服务端IP> 7891,输入文本验证双向传输

4. 防火墙/iptables检查

Raspberry Pi可能默认开启防火墙,阻止UDP端口7891的数据包。执行以下命令临时关闭防火墙(测试用)或开放端口:

# 临时关闭防火墙
sudo iptables -F
# 永久开放UDP 7891端口
sudo iptables -A INPUT -p udp --dport 7891 -j ACCEPT
sudo iptables -A OUTPUT -p udp --sport 7891 -j ACCEPT

5. 代码优化建议

  • 服务端添加错误检查:bind、recvfrom、sendto调用后检查返回值,用perror打印错误信息
  • 客户端recvfrom不要传NULL作为地址参数,至少传入空的sockaddr结构和长度,避免潜在问题
  • 添加超时机制,避免recvfrom无限阻塞

修改后的服务端关键代码片段:

/*Configure settings in address struct*/
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(7891);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 使用通配地址
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  

/*Bind socket with address struct*/
if (bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) == -1) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 17:40:39