是否可以使用 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 输出
可以看出:
- 没有发送 3 次,只有 2 次
- 第二次发送更多(垃圾)数据
有什么想法吗?
提前致谢
我知道在你的情况下你使用的是 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 解析。
我有这个代码
仅作为示例,以显示重新发送某些信息(重试)的意图
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 输出
可以看出:
- 没有发送 3 次,只有 2 次
- 第二次发送更多(垃圾)数据
有什么想法吗?
提前致谢
我知道在你的情况下你使用的是 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 解析。