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

使用C Socket实现nf2接口数据包环回时发送模块致程序死循环求助

解决NF2接口数据包环回程序无限循环问题

看起来你遇到的核心问题是程序发送出去的数据包又被自己的接收逻辑捕获了,形成了无限循环——你发一个包,自己收到,再发,再收,周而复始。下面我来拆解问题并给出修复方案:

问题根源

你创建了两个raw socket,并且都绑定到了nf2接口,同时接收所有类型的数据包(ETH_P_ALL)。当你调用write发送数据包后,这个包会被你的接收socket再次捕获,导致循环永远无法退出。

修复方案

我们可以通过以下几个关键改动来解决问题:

  • 只用一个raw socket完成收发操作,避免资源冗余和冲突
  • 获取nf2接口的MAC地址,过滤掉自己发送的数据包
  • 使用sendto替代write,更规范地发送raw数据包

修改后的完整代码

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <unistd.h>

int main() {
    int saddr_size, data_size, bytes_sent;
    struct sockaddr_ll saddr;
    unsigned char *buffer = malloc(65535);
    int count = 0;
    
    // 只用一个socket完成收发,AF_PACKET + SOCK_RAW可以同时处理收发
    int sock_raw = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sock_raw < 0) {
        perror("socket creation failed");
        return 1;
    }

    // 绑定到nf2接口
    struct sockaddr_ll bind_addr;
    memset(&bind_addr, 0, sizeof(bind_addr));
    bind_addr.sll_family = AF_PACKET;
    bind_addr.sll_protocol = htons(ETH_P_ALL);
    bind_addr.sll_ifindex = if_nametoindex("nf2");
    
    if (bind(sock_raw, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) < 0) {
        perror("bind failed");
        close(sock_raw);
        return 1;
    }

    // 获取nf2接口的MAC地址,用于过滤自己发送的包
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "nf2");
    if (ioctl(sock_raw, SIOCGIFHWADDR, &ifr) < 0) {
        perror("get MAC address failed");
        close(sock_raw);
        return 1;
    }
    unsigned char self_mac[ETH_ALEN];
    memcpy(self_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);

    while(1) {
        saddr_size = sizeof(struct sockaddr_ll);
        // 接收数据包
        data_size = recvfrom(sock_raw, buffer, 65536, 0, (struct sockaddr *)&saddr, (socklen_t*)&saddr_size);
        if (data_size < 0) {
            perror("recvfrom error");
            close(sock_raw);
            return 1;
        }

        // 过滤掉自己发送的包:判断数据包的源MAC是否等于接口自身MAC
        unsigned char *src_mac = buffer + 6; // 以太网帧头前6字节是目的MAC,接下来6字节是源MAC
        if (memcmp(src_mac, self_mac, ETH_ALEN) == 0) {
            // 这是自己发出的包,跳过
            continue;
        }

        printf("%d Received %d bytes\n", count, data_size);
        count++;

        // 发送数据包:使用sendto明确指定发送接口
        bytes_sent = sendto(sock_raw, buffer, data_size, 0, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
        if (bytes_sent < 0) {
            perror("sendto error");
            close(sock_raw);
            return 1;
        }
        printf("Sent %d bytes\n", bytes_sent);
    }

    close(sock_raw);
    free(buffer);
    return 0;
}

关键改动说明

  1. 合并收发Socket:不再创建两个socket,只用一个AF_PACKET类型的raw socket即可同时处理接收和发送,减少资源占用和潜在冲突。
  2. 过滤自发包:通过获取接口MAC地址,在接收后判断数据包的源MAC是否为自身MAC,如果是则跳过发送,避免形成循环。
  3. 使用sendto发送:相比write,sendto可以明确指定发送的接口信息,让raw数据包的发送逻辑更清晰可靠。
  4. 内存释放:添加了free(buffer),避免内存泄漏。

这样修改后,程序就只会转发从外部收到的数据包,不会陷入无限循环了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:42:46