【问题标题】:What's the best way to monitor a socket for new data and then process that data?监视套接字以获取新数据然后处理该数据的最佳方法是什么?
【发布时间】:2010-10-02 00:03:34
【问题描述】:

请原谅我的 C#.Net 新手身份。如果这很明显并且我在文档中遗漏了它,将不胜感激提供相关页面或示例代码的链接。

我正在开发一个可以接受来自 Java 应用程序的 TCP 套接字连接的应用程序。 (是的,这部分需要 Java。它是 Sun SPOT 设备,Java 是唯一的选择。)Java 应用程序将定期将新数据写入套接字,我的应用程序的工作是接收 byte[],转换将其转换为字符串,处理数据(更新 UI 等),并可能将数据转发到另一台运行类似 C#.NET 应用程序的计算机上。

这是我到目前为止所做的:现在,应用程序在启动时启动了一个打开套接字的线程。 Java 应用程序可以成功连接到套接字,以便正常工作。我正在查看 NetworkStream 的 beginRead 方法和 dataAvailablelengthCanRead 属性,但我不完全确定如何确定何时读取了一个数据包,通常约为 512 字节,但可能会有所不同。

如果 Java 应用程序将数据写入流或有数据积压(Java 应用程序将很快传递数据。)我如何确保一次只读取一个数据包?如果 Java 应用程序 null 在写入时终止数据,那会有帮助吗?够了吗?

最后,socket 只会接收一个连接,但我需要让它保持打开状态,直到出现错误或连接终止。处理这方面的最优雅的方式是什么?由于在 Sun SPOT 基站上运行的 Java 应用程序的快速启动(近乎实时)方面,我认为关闭并重新打开每个数据包都不会起作用。现在,当基站终止时,我的应用程序会大声而痛苦地死去。 :)

感谢您的阅读以及您可以提供的任何帮助。

【问题讨论】:

  • TCP。谢谢,我会修改帖子。

标签: c# multithreading .net-3.5 sockets sunspot


【解决方案1】:

我们解决此问题的方法是结合使用标记字符和固定宽度字段。前两个字节是整个数据包的长度标头。然后,我将数据包读入 byte[] 缓冲区,然后依靠我们自己的数据包结构来知道,例如,前两个字节分别被解释为单独的字段,然后字符串字段以 \n 字符终止(如果您在家得分,则为 0x0A)。然后,通过读取 8 个连续字节等来处理长数据字段。这对我们来说似乎工作得很好,但这显然是一种解决方案,即一个人可以控制套接字的两端,而不是只有一个能够控制一端。希望这对其他人也有帮助。

【讨论】:

    【解决方案2】:

    “如果 Java 应用程序将数据写入流或有数据积压(Java 应用程序将很快传递数据。)我如何确保一次只读取一个数据包? "

    请注意,不要假设您可以控制哪些数据最终进入哪个数据包。如果您尝试发送字节数据{ 'H', 'e', 'l', 'l', 'o' },则无法保证所有这些数据都将在一个数据包中发送。虽然极不可能,但每个数据包仍然可能只包含一个字节,因此您将在 5 个不同的事件中收到所有五个字节。关键是,不要以这种方式依赖数据包。相反,定义您自己的消息结束终止符,并将所有传入数据简单地放入某种字节缓冲区中,并使用另一个函数来检测是否存在这些终止符中的任何一个。如果是这样,请阅读该终结者。例如,假设您从 Java 应用程序调用相应的 send 方法两次,其中包含以下数据:

    { 'H', 'e', 'l', 'l', 'o', '\0' }
    { 'W', 'o', 'r', 'l', 'd', '\0' }
    

    您的应用程序应该如何准备接收数据应该是这样的:

    Server receives { 'H', 'e', 'l' }
    Data stored in byte buffer { 'H', 'e', 'l' }
    Check byte buffer for message terminator '\0'. None found. Buffer unchanged, no message processed.
    Server receives { 'l', 'o', '\0', 'W' }
    Data stored in byte buffer { 'H', 'e', 'l', 'l', 'o', '\0', 'W' }
    Check byte buffer for message terminator '\0'. 1 found, extracted message { 'H', 'e', 'l', 'l', 'o' } and buffer updated { 'W' }
    

    因此,虽然这并不完全是您最初问题的答案,但我认为它应该会推动您朝着正确的方向前进。

    您可能会遇到的一件事是,根本没有任何字符不能成为数据而不是消息终止符。例如,许多文件包含数据 \0,因此这些文件会破坏您的消息检测。通常如何处理这个问题是通过为您的协议创建一个标头规范并检测您是否期望一个标头(在这种情况下,查找 \0 将表示消息的结尾)或者您是否正在等待一定数量的数据(可以由收到的最后一个标头指定。)如果这没有意义,并且您认为您可能需要使用此技术,请告诉我,我会添加到此答案中。

    【讨论】:

    • 这非常有帮助。我对即将传入的数据的了解是它不会跨行,因此 \n 或 \0 字符将用作终端字符。这基本上是从 SPOT 传递到基础然后传递到我的 C# 应用程序的传感器数据读数。 IEEE 地址,传感器信息。
    • 我认为这个应该是公认的答案,尽管你做了一些不同的事情。
    • @jxpx - 您是否需要进一步澄清,或者这是您问题的可接受答案?
    • 不,我最终选择了不同的路线。在此处查看我上面的答案,我将其标记为已接受。
    【解决方案3】:

    当您读取流数据时,您需要一些标记/终止字符或已知大小的数据来确定何时停止读取和处理信息。空字符或换行符很常见。另一种技术是使用固定大小的标头来指定正文的长度。

    在您关闭它或另一端终止之前,套接字将保持打开状态,在这种情况下,您将在从您应该处理的套接字读取时遇到错误。如果在读取或写入操作期间从远程端关闭了套接字,NetworkStream 将引发 IOException。

    【讨论】:

    • 您需要通过检查每个字节来处理的事情。如果您使用标头方法,您可以为标头读取固定数量的字节。从中读取正文大小,然后以正文的固定大小再次调用 read。然后你就不需要检查所有的字节了。
    • 我上面关于读取固定大小的cmets适用于阻塞读取。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 1970-01-01
    相关资源
    最近更新 更多