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.

[参考译文] TM4C1294NCPDT:在保持空闲状态一段时间后、套接字不接收任何数据

Guru**** 2473260 points


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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/666084/tm4c1294ncpdt-socket-does-not-receive-any-data-after-remaining-idle-for-sometime

器件型号:TM4C1294NCPDT

我创建了一个 TCP 客户端、该客户端连接到 MQTT 代理并通过套接字接收数据。 我已使用示例 TCP 回显示例和 TI-RTOS NDK 堆栈来创建套接字并连接到代理。 在代理或服务器以4分钟的时间间隔发送数据之前,它的工作效果非常好,但当服务器在超过4分钟的时间间隔内发送或发布数据时, 套接字保持空闲状态、并且不接收来自服务器/代理的任何内容。哪个参数设置了所有这些内容...我在 tcpecho.cfg 文件中找到了一些类似的参数...我尝试增加这些参数、但它不起作用。如何在不干扰套接字空闲状态的情况下始终保持套接字接收?

谢谢

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

    您好 Prachi、

    是否可以尝试为套接字设置保持活动超时?

    这可以通过将 setsockopt() API 与 socket 选项 SO_keepalive 一起使用来实现。

    int skt;
    int optval;
    int optlen = sizeof (optval);
    
    // skt = socket (...);
    
    optval = 1;
    status = setsockopt (skt、SOL_socket、SO_keepalive、 optval、optlen);
    if (status =-1){
    //错误
    } 

    Steve

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

    您好、Steve、

    感谢您的回复!

    我已经设置了这样的参数  

    服务器=套接字(AF_iNet、SOCK_STREAM、IPPROTO_TCP);
    if (server ==1){
    System_printf ("错误:未创建套接字。\n");
    返回;
    }
    
    memset (localAddr、0、sizeof (localAddr));
    localAddr.Sin_Family = AF_iNet;
    localAddr.Sin_addr.s_addr = inet_addr ("198.41.30.241");
    localAddr.sin 端口= htons (1883);
    
    
    /*检查 keepalive 选项的状态*/
    if (getsockopt (server、IPPROTO_TCP、SO_keepalive、&optval、&optlen)< 0)
    {
    关闭(服务器);
    }
    printf ("SO_keepalive 为%s\n",(optval? "on":"off");
    
    
    /*将选项设置为活动*/
    optval = 1;
    optlen = sizeof (optval);
    if (setsockopt (server、IPPROTO_TCP、SO_keepalive、&optval、optlen)< 0)
    {
    关闭(服务器);
    
    }
    printf (so_keepalive 在 socket 上设置\n");
    
    
    /*再次检查状态*/
    if (getsockopt (server、IPPROTO_TCP、SO_keepalive、&optval、&optlen)< 0){
    
    关闭(服务器);
    
    }
    printf ("SO_keepalive 为%s\n",(optval? "on":"off");
    
    
    while (connect (server、(struct socaddr *)&localAddr、sizeof (localAddr))< 0){
    SysCtlDelay (400000);
    }
    
    data.clientID.Cstring ="me";
    data.keepAliveInterval = 0;
    data.清洁= 1; 

    tcpecho.cfg 文件具有 TCP 模块的默认时间设置、例如:

    套接字保持空闲时间(0.1秒)= 72000

    套接字保持活动探测器间隔时间(0.1秒)=750

    套接字保持活动探测器超时(0.1秒)=6000

    根据我的理解 、"套接字保持空闲时间"允许您的套接字即使在此处设置的时间内保持空闲状态也可保持活动、而"套接字保持活动探头间隔时间"用于设置将保持活动探头包发送到服务器的时间间隔(...或服务器将其发送给对等设备?) 第三个参数“Socket Keep Alive probe timeout”将决定在断开连接之前应答 Keep Alive probe 请求的时间..... 我对吗?

    我已使用 setsockopt API 创建用 SO_keepalive 值指定的套接字、其中包含在 tcpecho.cfg 中配置的上述值。 我希望即使在闲置一段时间后仍保持套接字的活动状态...我不想在闲置一段时间后关闭套接字、而是应该在不活动之后从服务器接收数据。

    我如何实现此类功能?

    此致、

    Prachi

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

    您好 Prachi、

    您在 setsockopt()调用中的级别似乎错误:

    [引用 USER="Prachi Patil"] if (setsockopt (server、IPPROTO_TCP、SO_keepalive、&optval、optlen)< 0)

    该选项应为 SOL_SOCKET。

    上述呼叫是否未出现错误?

    Steve

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

    您好、Steve、

    [引用用户="Steven Connell"]

    该选项应为 SOL_SOCKET。

    上述呼叫是否未出现错误?

    [/报价]

    在我 将该选项设置为"IPPROTO_TCP/"之前、我没有收到错误、然后代码按应有的方式执行、我通过 setsockopt API 之前和之后通过 getsockopt API 检查 SO_keepalive 选项是否在套接字上设置。

    现在、我已将选项设置为"SOL_SOCKET"、但在某些接收不活动后、套接字仍然不会保持活动状态。 tcpecho.cfg 仅将以下三个参数配置到由 SO_keepalive 选项指定的套接字...您能指导我为套接字设置这些参数以使其长时间保持活动状态吗?

    1. 套接字保持空闲时间
    2. 套接字保持活动探头间隔时间
    3. 套接字保持活动探测器超时

    谢谢

    Prachi

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

    [引用 USER="Prachi Patil]]根据我的理解 、"套接字保持空闲时间"允许您的套接字即使在此处设置的时间内保持空闲状态、也仍然保持活动状态、 而"套接字保持活动探测器间隔时间"用于设置将保持活动探测器数据包发送到服务器(...或服务器将其发送到对等设备?)的时间间隔 第三个参数“Socket Keep Alive probe timeout”将决定在断开连接之前应答 Keep Alive probe 请求的时间..... 我对吗?[/引述]

    您已走上正轨。

    TCP Keep 探头用于检查"另一侧"是否仍然存在、因为在一段时间内未听到另一侧的任何声音。 例如、一个空闲套接字在几分钟内没有看到任何通信、无法知道这是因为另一侧没有任何发送、还是连接中断。 保持探头有点"嘿、你还在吗?" 消息。 如果你得到了回复、那么你就知道连接仍然存在并且没有被切断。 任何一端(服务器或客户端)都可以在连接闲置一段时间时发送保留探测器。

    那么、我们如何定义"一段时间内怠速运转"? 这将使我们看到您找到的参数。

    1. 保持空闲时间-这是在开始担心连接中断之前允许连接处于空闲状态的时间长度。 这段时间后、堆栈决定开始向外发送 Keep 探头、以查看另一侧是否仍然存在。
    2. 保持间隔-一旦堆栈开始发送 Keep 探测器(如上面#1中所述)、此参数将控制发出这些探测器的频率。
    3. 保持最大空闲-如果您愿意、这是第二个空闲时间。 一旦 Keep 探测器开始运行、如果我们没有收到对探测器的任何响应、这是关闭连接之前堆栈将等待的最长时间。 基本上、如果达到此时间、我们假设连接断开。

    [引用 USER="Prachi Patil"]但在某些接收不活动之后,套接字仍然不会保持活动状态

    嗯... 这里仍然有一些问题。 如果您确实正确设置了“保持活动”选项,并且默认的保持闲置时间为72000 (2小时),则应用程序甚至不应启动“保持活动 ”探测过程,直到在不活动2小时后... 但您在4分钟后会看到一个问题。

    [报价 USER="Prachi Patil"]当服务器在超过4分钟的时间间隔内发送或发布数据时,套接字保持空闲状态,并且不从服务器/代理处接收任何数据

    您现在可以进行 Wireshark 捕获吗? 我想看看堆栈是否使用某种错误数据包响应服务器。

    请注意,您需要使用适当的网络拓扑来捕获数据包。 您的 MQTT 服务器是否在您使用的 PC 上运行? 还是 WAN 上的某个远程服务器?

    希望您连接到 Linux 盒或某种 PC 上的 MQTT 服务器。 然后、您可以在该 PC 上运行 Wireshark。 否则,您需要使用具有端口镜像的集线器或交换机。

    Steve

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

    还有一件事需要说明:

    [引用 user="Prachi Patil"] printf ("SO_keepalive 为%s\n",(optval? "on":"off");

    这样的 printf()调用对于实时系统可能会有问题。 您应该改用 RTOS 友好的打印方式。

    例如、您可以执行以下操作:

    #include

    system_printf ("so_keepalive 为%s\n"、(optval? "on":"off");

    system_flush ();//可以"稍后再执行"将缓冲区刷新为标准输出。

    Steve

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

    您好、Steve、

    非常感谢您详细的回复!

    [引用用户="Steven Connell"]Hmm... 这里仍然有一些问题。 如果您确实正确设置了“保持活动”选项,并且默认的保持闲置时间为72000 (2小时),则应用程序甚至不应启动“保持活动 ”探测过程,直到在不活动2小时后... 但您在4分钟后会看到问题。[/quot]

    我使用 tcphandler 任务 连接到 MQTT,如...

    空 tcpHandler (UARg arg0、UARg arg1)
    {
    // fdOpenSession(TaskSelf());
    
    内部 状态;
    内部 客户;
    //内部 服务器;
    struct sockaddr_in localAddr;
    struct sockaddr_in clientAddr;
    内部 光度;
    内部 optlen = sizeof (optval);
    socklen_t addrlen = sizeof (clientAddr);
    Task_handle 任务处理;
    Task_Params 任务参数;
    ERROR_Block EB;
    
    MQTTPacket_connectData 数据= MQTTPacket_connectData_initializer;
    int RC = 0;
    int mysock = 0;
    unsigned char buf[200];
    int buflen = sizeof (buf);
    int msgid = 1;
    MQTTString topicString = MQTTString_initializer;
    int req_qos = 0;
    char*有效载荷="mypayload";
    int payloadlen = strlen (payload);
    int len = 0;
    
    
    
    服务器= NDK_socket (AF_iNet、SOCK_STREAM、IPPROTO_TCP);
    if (server ==1){
    System_printf ("错误:未创建套接字。\n");
    返回;
    }
    
    memset (localAddr、0、sizeof (localAddr));
    localAddr.Sin_Family = AF_iNet;
    localAddr.Sin_addr.s_addr = inet_addr ("198.41.30.241");
    localAddr.sin 端口= htons (1883);
    
    
    /*检查 keepalive 选项的状态*/
    if (ndk_getsockopt (server、SOL_socket、SO_keepalive、&optval、&optlen)< 0)
    {
    关闭(服务器);
    }
    system_printf ("so_keepalive 为%s\n"、(optval? "on":"off");
    system_flush();
    
    
    /*将选项设置为活动*/
    optval = 1;
    optlen = sizeof (optval);
    if (ndk_setsockopt (server、SOL_socket、SO_keepalive、&optval、optlen)< 0)
    {
    关闭(服务器);
    
    }
    system_printf (so_keepalive 在套接字上设置\n");
    system_flush();
    
    /*再次检查状态*/
    if (ndk_getsockopt (server、SOL_socket、SO_keepalive、&optval、&optlen)< 0){
    
    关闭(服务器);
    
    }
    system_printf ("so_keepalive 为%s\n"、(optval? "on":"off");
    system_flush();
    
    while (ndk_connect (server、(struct socaddr *)&localAddr、sizeof (localAddr))< 0){
    SysCtlDelay (400000);
    }
    
    data.clientID.Cstring ="me";
    data.keepAliveInterval = 0;
    data.清洁= 1;
    
    SysCtlDelay (400000);
    }
    Len = MQTTSerialize_connect (buf、buflen、&data);
    RC = NDK_SEND (服务器、buf、len、NULL);
    
    /*等待 connack */
    if (MQTTPacket_Read (buf、buflen、Getdata)== CONNACK)
    {
    unsigned char sessionPresent、connack_RC;
    
    if (MQTTDeserialize_connack (&sessionPresent、&connack_RC、buf、buflen)!= 1 || connack_RC!= 0)
    {
    system_printf ("无法连接、返回代码%d\n"、connack_rc);
    system_flush();
    返回;
    }
    }
    其他
    返回;
    
    /*订阅*/
    topicString.cstring ="sub9/129";
    Len = MQTTSerialize_subscribe (buf、buflen、0、msgid、1、 topicString、&req_QoS);
    
    RC = NDK_SEND (服务器、buf、len、NULL);
    if (MQTTPacket_Read (buf、buflen、Getdata)== SUBACK)//等待子 ACK
    {
    unsigned short subsgid;
    int 子计数;
    Int granted_qos;
    
    RC = MQTTDeserialize_suback (&subsgid、1、&subcount、&granted_qos、buf、 Buflen);
    如果(granted_qos!= 0)
    {
    system_printf ("已授予 QoS!= 0、%d\n"、已授予 QoS);
    system_flush();
    返回;
    }
    }
    其他
    返回;
    
    /*循环获取订阅主题上的 msgs */
    // topicString.cstring ="TIVA/e";
    while (1)
    { /* translation_Getdata()有一个内置的1秒超时时间,您的里程数将有所不同*/
    if (MQTTPacket_Read (buf、buflen、Getdata)== publish)
    {
    unsigned char DUP;
    内部 QoS;
    保留无符号字符;
    unsigned short msgid;
    int payloadlen_in;
    unsigned char* payload_in;
    // int rc;
    MQTTString receivedTopic;
    
    RC = MQTTDeserialize_publish (&DUP、&QoS、&lerved、&msgid、&receivedTopic、
    &PAYLOAD_IN、&PAYLOAD_IN、buf、buflen);
    system_printf ("消息到达:%.*s\n"、payloadlen_in、payload_in);
    system_flush();
    }
    }
    System_printf ("断开连接\n");
    system_flush();
    Len = MQTTSerialize_disconnect (buf、buflen);
    RC = NDK_SEND (服务器、buf、len、NULL);
    关闭(服务器); 
    // fdClose (server); // fdCloseSession (TaskSelf ());

    由于我不熟悉 RTOS、我无法确定问题所在。 我认为空闲任务会在一段时间后运行,如果我们在空闲任务运行期间在 MQTT 上发布一些数据,则 TCP 客户端不会收到它...如何停止空闲任务运行并使 tcpphandler 任务始终运行并保持套接字活动?

    我捕获了 MQTT 协议的 Wireshark 网络流量、在这里我以超过4分钟的间隔发布了两条消息... 但 tm4c129没有收到第二条消息,但在网络流量中可以看到第二条已发布消息。此外,在不活动4分钟后,还可以看到 ping 请求和 ping 响应。。。。。。如果空闲任务开始运行,则可能会发生这种情况。 套接字不能从套接字接收或读取?

    我还想在 while (1)循环中打开和关闭套接字...这是否有用?

    e2e.ti.com/.../Wireshark-MQTT-ping-snippet.zip

    Prachi

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

    您好 Prachi、

    [引用 user="Prachi Patil"]我认为空闲任务会在一段时间后运行,如果我们在空闲任务运行期间在 MQTT 上发布一些数据, TCP 客户端未接收到它...如何停止空闲任务运行并使 tcpphandler 任务始终运行并保持套接字活动?[/quot]

    空闲任务始终在 TIRTOS 中运行。 它只是一个后台线程、在没有其他线程运行时运行。 您可能会暂停程序、看到 PC 处于空闲线程中、并认为您只是处于空闲线程中。 虽然这可能是事实、但系统中可能还有其他几个任务线程在运行、并且操作系统正在它们之间切换。

    您是否使用过 ROV 工具? 当您发现此问题时、请尝试一下。 您可以在 CCS 中的"Tools -> ROV"下找到它。 请注意、您必须停止处理器才能使用 ROV (它是一种停止模式工具)。

    打开它后、您可以单击任务模块以在应用程序中查看任务线程及其当前执行状态。 这是一个非常有用的工具、我将在下面插入一个屏幕截图。

    您应该在任务视图中看到 tcpHandler 任务。 如果您不这样做、则意味着它已退出、下一步是找出原因。

    如果未退出,则可能被阻止,可能是在 while 循环中调用 MQTTPacket_read()时。

    无论如何、请尝试一下。 如果您也能够执行此操作、请将您在 ROV 的任务视图中看到的内容的屏幕快照发布回。

    接下来、我对您的代码进行了快速扫描、并提出了一些建议:

    [报价用户="Prachi Patil"]// int 服务器;

    [报价用户="Prachi Patil"]服务器= NDK_socket (AF_iNet、SOCK_STREAM、IPPROTO_TCP);[/报价]

    "erver"是否是全局变量? 我看到上面注释的声明。  此外、NDK_socket 返回类型句柄、因此应将服务器声明为该类型(而不是 int)。 很容易与 NDK_socket ()和其他"POSIX 类"套接字 API 混淆。 它们非常相似、但有一些差异、例如返回的类型和参数类型。

    [引用 user="Prachi Patil"] if (server ==1){

    我会将其更改为"if (server = invalid_socket){"

    Prachi Patil 说:
    Close (服务器);
    // fdClose (server);

    这是 NDK_socket() API 与其 POSIX 表弟 socket()之间的另一个区别(例如在典型的 Linux 应用中使用)。 POSIX 样式 API 返回一个整数文件描述符,您应该在该描述符上调用 close()。 但是对于 NDK_socket() API,它返回一个指针,并应通过调用 fdClose ()来关闭。 再说一次、这很容易被混淆。

    因此、我会将您的所有 close (server)调用更改为 fdClose (server)

    实际上,由于其中一个 close()调用,应用程序可能会失败。

    [引用 USER="Prachi Patil"]   if (MQTTPacket_Read (buf、buflen、Getdata)=SUBACK) //等待子 ACK */
       {

    (笑声)

      }
       其他
           返回;

    [/报价]

    查看:

      其他
           返回;

    进行编程。 是否可以将其更改为在此处打印一条消息? 您可能会单击此返回并退出 tcpHandler 任务线程。

    Steve

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

    它的状态是什么?

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

    我已经赶上了另一项任务、因此没有时间寻找 Steven 建议的解决方案、但由于我将不需要当前的工作、我也将检查此项工作、并将您和 Steven 保持发布状态。

    任何方式...非常感谢您的帮助!

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

    您好 Prachi、

    对此进行了任何更新?

    Todd

    [4/16更新...我将此标记为 TI 认为已解决。 您可以发布回复以重新打开回复。]

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

    感谢更新...我没有时间解决这个问题(赶上另一个项目)... 因此,最好将这一问题标记为解决,以便无限期地返回,并处理这个问题。 必要时我会重新打开它。

    Prachi