This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TMS320C6678:当传入的碎片数据时、NDK 堆栈丢失 UDP 封包

Guru**** 2589280 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/585975/tms320c6678-ndk-stack-lost-udp-packet-when-incoming-fragmented-data

器件型号:TMS320C6678

您好!

我的客户正在尝试使用具有 NDK 协议栈的 UDP 协议通过以太网电缆捕获摄像头数据。

他们遇到的问题是、如果传入的数据被碎片化到多个数据包、他们的应用程序将丢失最终的数据包。 请注意、他们使用 ProcSDK 版本03.01.00进行开发。

他们的应用程序使用 BSD 套接字 API 进行数据捕获,其代码如下所示:

#define APP_IP_ADDRESS"192.168.1.2"
静态常量 uint16_t GVSP_APP0_UDP_PORTNUM= 60001;
静态常量 uint16_t STREPORT_PACKETSIZE= 1400;
静态常量 uint32_t IP_HEADER_SIZE= 20;// IP 头大小 uint16_t
头大小= uint8_tudp_t_t_size;
静态 INT8_uDP_t_t_size =静态头文件大小= 8;静态 INT8_tu_v3_uDP_t_size = u_v3_t_size = uDP_int8头// GVSP 报头大小
静态 const uint32_t GVSP_PACKETSIZE= STREE_PACKETSIZE - IP_HEADER_SIZE - UDP_HEADER_SIZE;// UDP 数据包大小。 接收缓冲区大小

静态空 CaptureStream (const socket StreamSock){

char buff[GVSP_PACKETSIZE]={0};

while (TRUE){
int bytes = recv (streamSock、buff、sizeof (buff)、0);
// int bytes = recv (sizamsock、MSG、eof (buff

)、如果无效、则为0)、则为0)




}


静态 bool InitStreamChannel (socket &StreamSock,struct sockaddr_in &StreamAppAddr){fdOpenSession(

TaskSelf());

//***** 创建套接字 //
StreamSock = socket (AF_iNet、SOCK_DGRAM、0);
if (StreamSock = INVALID_SEocket){
ReleaseSocket (StreamSock);
CDebugLog (DBM_camera2、"无法创建 UDP 套接字\r\n");
return false;
}

memset (&StreamAppAddr、Addr)
StreamAppAddr.Sin_Family = AF_iNet;
StreamAppAddr.Sin_port = htons (GVSP_APP0_UDP_PORTNUM);
inet_pton (AF_iNet、APP_IP_ADDRESS、&(StreamAppAddr.Sin_addr));

if (bind (StreamSock、(struct sockdr *)&StreamAppAddr、sizeof (StreamAppAddr))== socket_error){
ReleaseSocket (StreamSock);
CsockLog (DBM_camera2、"无法绑定 UDP 套接字\r\n");
return false;
}


= socksockpit (stroct、socknum、enchar) sizeof (recnum)= socket_error){
返回 false;
}

#if 1
struct timeval 超时;
timeout.tv_sec = 0;
timeout.tv_usec = 100 * 1000;

if (setsockopt (StreamSock、SOL_socket、SO_RCVTIMEO、(char *)&timeout、 sizeof (timeout)!= 0){
return false;
}
#endif
return true;
}


class StreamChannelTask:public nTask{
public:
virtual void TaskFunc (uint32_t arg){

socket StreamStreamStrek;
struct socksaddr_in SocketAppAddr;

while (!InitStreamChannel (uintx32_t r)

r) sockeStream (capture/r)、sockn)、socksockeRit



(unit (sockn)、sockeStream (unit)、sockn)、sockeRit (unit、sockn)


//生成捕获结束事件
CSetEvent(camera.EndCaptureEvent);
}


}}}}}}}}}{streamchanneltask;

 这里是它们的调试状态。

-输入数据缓冲区在其应用程序中为1372字节。  这是传入数据流的最大数据包有效载荷。 这已通过 Wireshark 验证。
他们只是尝试增加这个接收缓冲器,但它没有帮助。
-如果从摄像机发送非碎片数据,即,如果可以 将传入的数据容纳在单个 UDP 有效载荷中,则捕获的数据似乎总是正确的。
由于摄像头应用程序的原因,传入的数据始终是大量的数据,因此传入的数据基本上被碎片化成如此多的数据包。 在这种情况下,最后一个数据包的 recv()函数总是被卡住。 我的意思是、控制不会返回到应用程序(在 recv 函数中阻止)、最终应用程序会丢失最终数据。
-如果他们将此应用程序代码转换为 Windows 应用程序(使用 winsocket),他们总是能够成功地从相机捕获所有数据。
-堆栈线程在其应用程序中具有最高的优先级。 任何其他用户线程都使用较低的优先级。
即使在通信中引入了数据包间延迟,他们仍然会看到问题。 最后一个数据包始终丢失。
-更改为具有多个 PBM 缓冲器不起作用。
-使用 SO_RCVLOWAT 和 SO_RCVTIME 等套接字级别选项 不起作用。
-使用诸如 MSG_DONTWAIT 之类的 recv API 版本选项不起作用。

您对此问题是否有任何疑问?  请提供任何帮助。  
由于从摄像头捕获数据对于其应用非常重要、因此我们需要尽快提供解决此问题的建议。

此致、
Naoki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Naoki、

    我已通知 RTOS 团队。 他们的反馈将在此处发布。

    此致、
    Yordan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!

    我想与您确认的是:测试仅涉及网络中的常规大小数据包、图片中没有大型数据包? 摄像机只能生成大小为~1400字节的数据包、并以较小的数据包进行碎片整理。 或者、摄像机可以生成~1400字节的碎片数据包(因此完整数据包是大型数据包)? 测试中使用的 NDK 版本是什么?

    我知道 NDK 支持 IP 分段。 假设您有一些碎片数据包的 Wireshark 捕获、您是否有任何可将其发送到应用程序的受控环境? 然后、在 CCS 项目表达式窗口中、您可以检查全局统计信息"IPS"以了解 IP 层发生了什么情况? 统计信息在 ti\NDK\inc\stack\inc\ipif.h 中定义 您还可以检查 UDP 层统计信息"UDPS"。

    此致、Eric
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Eric

    感谢你的帮助。

    回答您的问题;

    -是的,数据包大小是固定的。 这些不是大型数据包。
    -是的、摄像机只能生成大小为~1400字节的数据包、并且在较小的数据包中进行碎片整理。  
      为了供您参考、我将传入的数据包总结为附件。
    -我没有环境可以重新创建我身边的问题。 我`ll 我的客户在其环境中检查 IPS 和 UDPS 变量。
    他们还尝试了 ProcSDK 版本03.02.00,因此 NDK 版本应为2.25.0.9。

    此致、
    Naoki

    e2e.ti.com/.../capture_5F00_information.pptx

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Eric

    我在 第4页添加了 IPS 和 UPDS 日志;

    e2e.ti.com/.../capture_5F00_information_5F00_170406.pptx

    如您所见,在以后的数据包中没有变化... 这是预期行为吗? 请您向我们提供深入的见解以便进一步调查吗?

    此致、
    Naoki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!

    我不理解您进行实验的最后一页:
    -序列号5066、5067、5068是正常数据包
    -为什么 IPS 统计数据总数和交付情况保持不变? 我希望它们每次递增1。

    他们是否有一个工具来播放一个数据包、记录 IPS 和 UDPS、然后播放下一个数据包、从而了解数据包的质量如何提高 NDK 统计数据?

    此致、Eric
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Eric

    我创建了一个主机工具、用于在 UDP 协议上发送数据。 请查找附件。

    e2e.ti.com/.../testudpsend.c

    e2e.ti.com/.../sockets.h
    您可以使用 MinGW 对其进行编译。 可以使用以下命令:

    gcc testudpsend.c -o testudpsend.exe -lwsock32 -lws2_32 -Wall
    下面是用法:
    testudpsend.exe [IPv4或 IPv6地址][端口][要发送的字节大小]
    使用此工具、如果我们发送足够大的数据以进行碎片整理、Wireshark 将显示"碎片化 IP 协议"、如下所示。
    如果返回上一帖子中的共享 ppt、您将注意到没有相同的指示器。
    因此,我们得出的结论是,输入的数据实际上从未碎片化... 我们很抱歉输入了错误的信息。
    因此、现在传入的数据是正常数据包。
    但问题仍然存在。 如何对此问题进行调试? 您是否有任何其他状态标志用于指示数据包丢失错误?
    此外、我认为我们需要调试 NIMU 层来解决该问题。 您对 NIMU 调试有什么想法吗? 供参考、我可以自行构建 Nimu Transport 以进行调试。
    此致、
    Naoki
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    其他信息。

    下面是 Wireshark 捕获传入数据包的后一部分。 请将扩展名从.txt 交换为.pcapng。

    e2e.ti.com/.../packet_5F00_data_2D00_end_2D00_edit.txt

    您需要安装 Wireshark 以供参考。
    正如我之前提到的、最后一个数据包(length=66)有问题。
    您对此有任何怀疑吗? 尤其是收割台零件。 您是否看到任何不正确的情况导致报头信息中的数据包丢失?

    此致、
    Naoki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    为了区分这个问题,我建议使用所附的尼姆。 请查看代码(请参阅 MYDEBUG 定义)。
    您会注意到,Ninu 会转储传入的数据包,特别是86、1414、662和66 (问题中的最后一个数据包)大小的 UDP 数据包。
    此外、为了实现更合适的缓存操作、我从 CSL 缓存 API 更改为 SYS/BIOS 缓存 API。 Nimu 使用围栏指令、但仅使用单个月。 如您所知、C6678具有 mfence 指令的勘误表、用户不应使用单个 mfence、而是使用双 msence。 (请查看勘误文档)。 我相信 SYS/BIOS 缓存 API 能够正确处理这个问题(实际上、我看到双 msfence 指令集成在 BIOS_6_46_01_38源代码中。 这`s 我更改了 Nimu 中的高速缓存集成。

    好的、关于我们的观察结果。

    当问题发生时,它显示了 g_pktLenHistory 和 g_pktLenHistory2数组中实际存在的最后一个数据包(length=66)。 正如您在代码中看到的那样、当数据包被传入时、NIMU 会在将这些数据包插入 HW 队列后将上述目标数据包转储到 g_pktLenHistory、然后向 NDK 堆栈提出事件、以通过 STKEVENT_SIGNAL 函数检索接收到的数据包。 通过此回调、NDK 堆栈尝试通过 EmacPtService 函数从硬件队列获取接收到的数据包。 此时、NIMU 将接收到的数据包转储到 g_pktLenHistory2数组、然后再返回到 NDK 堆栈。

    因此、当最后一个数据包(长度=66)丢失问题发生时、Nimu 实际上成功地获得了最后一个数据包。  我将缓存相关集成从 CSL 更改为 SYS/BIOS API、但我发现它不能帮助解决此问题。 因此、这可能与 Nimu 中的缓存问题无关。 `m 我、我想知道 NDK 堆栈是否错过了最后一个数据包... 如何进行下一次调试? 我们一直在解决这个问题2周。 请帮我们解决问题。

    提前感谢您。

    Naoki

    e2e.ti.com/.../8206.nimu_5F00_eth.c

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Naoki、

    很抱歉,我上周不在办公室。 正如您进行了大量调试并确认 NIMU 驱动程序将最后一个数据包发送到 NDK 时所做的那样。 则调试将位于 NDK 内部。 这是哪个 NDK 版本?

    我相信 NDK 中的调用路径是 NIMUReceivePacket (在 ti\NDK\STACK_Nimu\Nimu.c 中)=== >IPRxPacket (用于以太网报头0x0800)=== >上调 ips.total++=== >UdpInput()。

    您可以使用上面的 NDK 函数在应用程序中设置断点、以查看在向 DSP 播放最后几个数据包时调用了哪些数据包以及统计信息如何增加(IPS、UDPS)。

    此致、Eric
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Eric

    感谢您的回答。 他们尝试了 ProcSDK 版本03.01.00 (NDK 2.24.3.35)和03.02.00 (NDK 2.25.0.9)、但他们说存在相同的问题。 `m 深入研究 NDK 堆栈、我现在使用调试选项重建 NDK (仍在继续... 重建 NDK 需要大量时间... )。 正如我在上一篇文章中所说的、我可以捕获在 Nimu 层接收最后一个数据包的确切时刻。 如果我们为此设置断点、我们可能会看到您提到的用于后续执行的调用路径。 我希望这将有助于获得新的结果... `ll 我们在这方面取得任何进展后、我会立即向您进行更新。 同时、如果您对此问题有任何疑问、请更新我的信息。 请将此作为文件的一部分。

    此致、
    Naoki
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Eric、

    仅供您参考。 我缩小了 NDK 堆栈中的故障点、发现 UDP 数据包长度验证失败(UDPS.RcvBadLen 在最后一个数据包中递增)。 在最后一个数据包中、UDP 报头长度似乎不正确。 他们现在正在检查这一点。  

    此致、
    Naoki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!

    感谢您的告知!

    此致、Eric