【问题标题】:Getting junk data in messages being sent with TcpClient and TcpListener在使用 TcpClient 和 TcpListener 发送的消息中获取垃圾数据
【发布时间】:2016-05-26 00:45:50
【问题描述】:

我还有其他几段使用相同代码运行的网络代码,我以前从未见过这种情况。接收缓冲区中的剩余字节被\0\0\0\0\0\0\0\0\0\0\填满

这个问题我见过几次,但一直没有答案。

TcpListener tlist = new TcpListener(IPAddress.Parse(ip.ToString()), 27275);

一旦建立连接。 . .

byte[] byData = new byte[textBox.Text.Length];
byData = System.Text.Encoding.UTF8.GetBytes(textBox.Text);
client.Send(byData, 0, byData.Length, 0);

以及接收端:

byte[] bufferRec = new byte[56];
tc.Client.Receive(bufferRec, 0, 56, 0);
string output = Encoding.UTF8.GetString(bufferRec);

我通常使用的缓冲区大小是 256,实际上我很少发送/接收 256 字节的数据,但我从来没有看到那些剩余的字节被垃圾数据填满,只有在这种情况下。

编辑:我当然可以简单地解析出垃圾数据,但它仍然是奇怪的行为,因为它没有理由。

【问题讨论】:

  • 请注意\0 是一个字符,而不是一个字节。我怀疑字节只是零。

标签: c# sockets tcpclient


【解决方案1】:

你忽略了Socket.Receive 的返回值,它告诉你已经读取了多少字节——这就像忽略Stream.Read 的返回值一样。因此,您的缓冲区包含的字节数比您实际收到的要多,因此您的字符串包含很多 U+0000 个字符。例如,获取长度为 16 的缓冲区和文本“HELLO”

buffer: 48 45 4C 4C 4f 00 00 00 00 00 00 00 00 00 00 00
Return value: 5 (5 bytes received)
Encoding.UTF8.GetBytes(buffer): "HELLO\0\0\0\0\0\0\0\0\0\0\0"
Encoding.UTF8.GetBytes(buffer, 0, 5): "HELLO"

基本上,您应该只使用缓冲区中包含已接收数据的部分。您也不应该假设对Receive(或Stream.Read)的单个调用将一次性读取所有数据,即使您的缓冲区足够大以接收它。 TCP 是一种流式传输协议,因此适用于流式传输的所有常规警告。如果您正在尝试编写面向消息的协议,则需要将其置于 TCP 之上(例如,通过具有指示下一条消息中有多少数据的“标头”部分)。

顺便说一句,直接从TcpClient 使用底层套接字并不是一个好主意——使用它的主要目的是您可以使用更高级别的抽象,例如GetStream()

【讨论】:

  • "基本上,您应该只使用缓冲区中包含已接收数据的部分。"没错,但这就是问题所在,它正在接收一堆垃圾数据,而不是在没有发送数据的地方什么也没有。缓冲区的典型行为不是用随机的 0 和斜线填充其余部分,它通常什么都不放在那里。
  • @user1438893: 你忽略了tc.Client.Receive 的返回值,所以我不知道你认为你是如何区分缓冲区中对应于数据的部分收到和没有的缓冲区部分。请注意,在调试器中显示为字符串中\0 的字符是 U+0000,这通常是解码字节 0 的结果。也许这让您感到困惑?
  • 当您将数据放入缓冲区inside Receive 调用时,我不确定您将如何处理Receive 的返回值。您是否应该丢弃原始缓冲区,检查大小,创建一个新缓冲区,然后将带有垃圾数据的缓冲区复制到具有正确大小的新缓冲区中?我很好奇,因为我从未见过或听说过任何这种行为是必要的。
  • @user1438893:不,您应该在调用GetString 时使用该数据,例如string output = Encoding.UTF8.Getstring(bufferRec, 0, bytesReceived)。尽管您应该注意,如果在一个调用中接收到字符串的一部分,而在另一个调用中接收到部分字符串,则可能会出现问题 - 特别是如果它位于不在 BMP 中的字符的中间。最好使用tc.GetStream() 并将其包装在StreamReader 中来处理它。我很惊讶你从未见过这个,因为它是所有基于流的操作的完全标准。
  • 哦,我明白了,在获取字符串时。那是有道理的。至于StreamReader,您是指发送包含下一个消息大小的初始消息,然后执行stream.Read(),直到收到所有字节?
猜你喜欢
  • 1970-01-01
  • 2017-04-09
  • 2014-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-17
  • 2013-04-16
相关资源
最近更新 更多