是否可以使用 udp_sendto 重新发送 UDP 数据包?

is it possible to use udp_sendto to resend an UDP packet?

我有这个代码

仅作为示例,以显示重新发送某些信息(重试)的意图

static char server_req [] = "INIT_PREVIEW";

static void
halt_task ( void )
{
    while ( true )
        ;
}
void
tst_task ( Tst_struct * tst_p )
{
    struct udp_pcb  *   my_udp  = NULL;
    struct pbuf     *   p       = NULL;
    ip4_addr_t          server_ip;

    // Wait 3 seconds for everything to start
    vTaskDelay ( pdMS_TO_TICKS ( 3000 ) );

    // Creates udp
    if ( (my_udp = udp_new ()) == NULL )
        halt_task ();

    udp_bind  ( my_udp, IP_ADDR_ANY, 10000 );

    if ( (p = pbuf_alloc ( PBUF_TRANSPORT, sizeof ( server_req ), PBUF_RAM )) == NULL )
        halt_task ();

    inet_aton ( "195.168.1.90", & server_ip );

    memcpy ( p->payload, server_req, sizeof ( server_req ) );

    for ( int tries = 1 ; tries <= 3 ; tries ++ ) {
        udp_sendto  ( my_udp, p, & server_ip, 10000 );
        vTaskDelay  ( pdMS_TO_TICKS ( 500 ) );
    }

    halt_task ();
}

这里是 wireshark 输出

可以看出:

有什么想法吗?

提前致谢

我知道在你的情况下你使用的是 lwIP 库,但更一般地说,“重新发送”UDP 数据包非常容易(这也应该适用于 lwIP) :

void test(void) {
  int sockfd = init_socket_somehow();
  bind_socket_somehow(sockfd);

  // example loop that resends packets the same way they are received:
  while (1) {
    struct sockaddr sender; // this will store info about the sender
    socklen_t sender_size; // this will store sender address' size

    uint8_t buff[500]; // this will store the UDP packet contents
    size_t buff_size; // this will store buff's size

    ssize_t bytes = recvfrom(sockfd, buff, 500, 0, &sender, &sender_size);

    if (bytes > 0) {
      buff_size = (size_t) bytes;

      // we have received some data via UDP, now we will resend the same data back to the sender
      sendto(sockfd, buff, buff_size, 0, &sender, sender_size);
    }
  }
}

注意:将这段代码更像是伪代码(它只显示逻辑;不进行错误检查);你可以调整它以便在你的程序中使用它。

编辑:

前面的代码片段重新发送收到的数据包,这不是你问的,所以下面的代码片段可以用来多次重新发送同一个数据包:

// send packet "packet" three times
uint8_t packet[100]; // fill the packet with some content to send
struct sockaddr dest = ...; // get the destination address and port somehow
socklen_t dest_size = ...; // and also the destination address' size

for (size_t i = 0; i < 3; i += 1) {
  sendto(sockfd, packet, 100, 0, &dest, dest_size);
}

我在 Internet 上四处查看,正如您在回答中所指出的那样,lwIP 库在某些情况下表现得很奇怪。

但我还发现 lwIP 支持 BSD 套接字接口(即:我在代码片段中使用的相同功能,并且在 Linux 和其他操作系统上广泛使用:socket()bind()recvfrom()sendto() 等),所以我的建议是在使用 lwIP 库时使用 BSD 套接字函数,以便一个(希望)更可预测的行为。

这个 link 应该可以帮助您将 BSD 套接字 API 与 lwIP 库一起使用。

我看过 lwip 库的源代码,udp.c 文件 “似乎”,udp_send(调用 udp_sendto)增加了添加 headers 的有效负载,因此不能再次调用它,期望将发送相同的内容。 对我来说很奇怪,实现 and/or 没有其他方法可以重试(这是像 UDP 一样的无连接协议的常见必要性) 无论如何,我将不胜感激任何补充信息的评论。

如果在 LWIP 的发送功能后恢复 pbuf 的状态,您就可以重新发送数据包。

结构 pbuf pbuf_saved;

pbuf_saved = *p;

仅在保留 PBUF 时才需要一次。

然后

udp_sendto..... *p = pbuf_saved;

这将恢复 pbuf 的 header 状态。 在分配期间,为所有协议和硬件层保留了净空,这会导致修改长度变量和有效负载缓冲区。 当然,您可以有选择地更新变量,但上面的方法是最简单的。 或者,您可以使用通过接口调用的直接发送方式,但这可能不如 ARP 未解析好,您可能无法获得新的 ARP 解析。