【问题标题】:Broken TCP messages损坏的 TCP 消息
【发布时间】:2011-11-07 14:23:37
【问题描述】:

我有一个简单的 TCP 服务器,它通过 GPRS 与一些设备通信。它可以正常工作很长时间没有任何问题。如今,设备(发送数据的客户端设备)发生了变化,它们发送的数据比以前多得多,TCP 消息的大小可能比以前大 10 倍。以前最大1024字节/数据消息,现在可以超过1万字节/消息。

正如我所见 - 正如 TCP 的性质所预测的那样 - 有很多消息“损坏”,因此不是完整的消息,只有一部分到达,稍后可能会到达另一部分,等等。

我认为我的服务器 - 基于异步模式 - 以错误的方式处理这种情况,我认为错误在我的 RecieveCallBack 函数中。我找不到正确的方法来处理这种情况。我该怎么做?

这是我的 RecieveCallBack 函数:

public void RecieveCallBack(IAsyncResult ar)
    {
        StateObject state = (StateObject)ar.AsyncState;

        try
        {
            Socket handler = state.WorkSocket;

            int read = handler.EndReceive(ar);

            if (read > 0)
            {
                var data = new byte[read];
                Array.Copy(state.Buffer, 0, data, 0, read);
            }
            else
            {
                if ((handler.Connected == false) || (handler.Available == 0))
                {
                    Close(state);
                    return;
                }
            }
        }
        catch (System.Net.Sockets.SocketException)
        {
            Close(state);
        }
        catch (System.Exception exc)
        {
            Debug.Assert(false, exc.Message);
            HandleSocketError(state, exc);
        }
    }

.net4/c#/vs2010

谢谢

更新: 客户端已连接,因为它们可以连接,因此取决于 GSM 网络。它们甚至可以连接数天,并且每分钟发送一次数据。客户端设备的协议不同,有不同类型的设备有不同的协议。其中一个在消息末尾有分隔符和CRC,其他没有。

【问题讨论】:

  • 每次你写“pocket”时,你的意思是“packet”吗?
  • @Tom:“整个数据组”的正确术语是“消息”。 Stephen Cleary 是正确的,您的问题的明显解决方案是您需要实现或使用消息框架协议。我建议你考虑使用 HTTP。
  • 您能否将有关该消息的已知信息添加到您的问题中(它们是否具有相同的格式,它们如何结束)。消息是在同一个连接上发送的,还是每次发送之间的连接都会终止?
  • 感谢您的回复。詹姆斯,我添加到我的问题作为更新。

标签: c# .net sockets asynchronous tcp


【解决方案1】:

您需要message framing。协议必须指定消息的大小——通常是已知的常量大小、长度前缀或使用消息分隔符。

【讨论】:

  • 这将假设改变设备发送 tcp/ip 数据包的方式,这是他无法做到的。
  • @James:他需要实现消息框架。我假设协议已经指定了被忽略的消息帧;如果协议没有指定消息帧,那么除了更改设备之外没有其他解决方案。
  • 不同意消息框架我同意当且仅当设备已经指定它时才是解决方案,我假设他们没有。有办法检查是否有更多数据在例如 socket.Available
  • @James:你会用额外的数据做什么???您必须有办法将其拆分为消息(除非它是流协议,但事实并非如此)。
  • "有很多消息是'损坏'的,所以不是完整的消息,只有一部分到达,稍后可以到达另一部分" - 听起来不像流式传输应用协议给我。
【解决方案2】:

TCP 中没有数据包这样的东西。它是一个面向流的协议。这意味着当您读取套接字时,您可以获得的数据比您预期的要少。您需要检查读取大小,如果读取时间较短,请再次读取其余数据。

以 C 语言为例:

int read_data(int sock, int size, unsigned char *buf) {
   int bytes_read = 0, len = 0;
   while (bytes_read < size && 
         ((len = recv(sock, buf + bytes_read,size-bytes_read, 0)) > 0)) {
       bytes_read += len;
   }
   if (len == 0 || len < 0) doerror();
   return bytes_read;
}

【讨论】:

  • "TCP 中没有数据包这样的东西。"充其量是误导,这取决于“在 TCP 中”的含义。我建议您根据 (IP) 数据包、(TCP) 段和 (UDP) 数据报进行声明。
  • -1 在 C 中使用 .NET 标记的示例,并且 TCP 具有数据包,因为它构建在 IP 堆栈之上。
  • James Kyburz:你错了。在应用层的 TCP 协议中没有任何数据包的概念。从用户/应用程序的角度来看,只有一个连续的字节流。因此,如果您想要数据的“数据包”,那么您必须在应用程序级别通过将诸如数据有效负载大小之类的报头信息预置到每次传输中来实现数据包。还有其他方法,例如使用某种数据编码和终止字节来指示数据包的结束。这方面的一个例子是基于文本的协议,它使用换行符来指示传输结束。
  • @MattH 查看我对 James 的评论。
  • @Robert S. Barnes:我知道你想说什么,我只是觉得你说得不好。您评论的“概念”和“在应用程序级别”的限定词在某种程度上解决了这个问题。
【解决方案3】:

我不确定应用程序的其余部分看起来如何,但我认为问题出在以下行:-

Array.Copy(state.Buffer, 0, data, 0, read);

如果数据包被分段,即同一连接多次调用接收回调,则 state.Buffer 将被最后收到的数据包覆盖,这是因为您将数据复制到 state.Buffer 中的偏移量 0。

根据您的需要,您可能需要在 StateObject 中添加更多状态 :),以便您可以附加数据而不是覆盖。

希望这个提示有所帮助。

【讨论】:

    猜你喜欢
    • 2019-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多