我在c#中使用套接字时遇到问题。这是一个例子。假设我发送数字1,然后我立即发送数字2.我遇到的问题有时是应该接收它的客户端将收到包含'12'的数据包。我想知道是否有内置的方法来区分数据包而不使用字符或其他东西来分离数据。
再次总结一下,我说了两个包。一个数字为'1',一个数字为'2'。 服务器接收数据为“12”的1个数据包。 我不想将数据包与字符分开,例如':1 :: 2:'或类似的东西,因为我并不总是能控制传入数据的格式。
有什么想法吗?
就像我这样做
client.Send(new byte[1]{'1'}, 1,SocketFlags.None);
client.Send(new byte[1]{'2'}, 1,SocketFlags.None);
然后在服务器端
byte[] data = new byte[1024];
client.Receive(data);
数据有时会以“12”返回,即使我做了两次单独的发送。
答案 0 :(得分:5)
TCP是一种流媒体协议,因此您将始终接收自上次读取以来已到达的任何数据,直至收件人端的流媒体窗口大小。该缓冲区可以填充从发送方发送的任何给定大小的多个分组接收的数据。
TCP Receive Window Size and Scaling @ MSDN
虽然您在示例中已经在接收端观察到1个包含2个字节的统一blob数据,但是可以接收1个字节乘1个字节的序列(由发送方发送),具体取决于网络条件,以及如果您正在执行非阻塞读取,则可以使用0,1和2字节读取的许多可能组合。在典型的非拥塞LAN或环回设置上进行调试时,如果发送端没有延迟,您几乎不会看到这一点。在网络堆栈的较低级别,有一些方法可以检测每个数据包的传输,但它们不会用于典型的TCP编程,并且不在应用范围内。
如果您切换到UDP,那么每个数据包都会在发送时收到,这符合您的期望。这可能更适合您,但请记住,UDP没有传送承诺,网络路由可能导致数据包无序传送。
您应该考虑分隔数据或找到其他方法来检测何时到达应用程序定义的数据单元的末尾,并坚持使用TCP。
答案 1 :(得分:5)
TCP / IP适用于流抽象,不 数据包抽象。
当您致电Send
时,您不发送数据包。您只是将字节附加到流中。
当您致电Receive
时,您不正在接收数据包。您只是从流中接收字节。
因此,你必须定义“消息”开始和结束的位置。只有三种可能的解决方案:
Receive
数据,直到获得那么多字节为止。Receive
长度前缀,直到它到达,解码它以获得消息的实际长度,然后保持Receive
消息本身。You must do the message framing yourself. TCP / IP无法为您完成。
答案 2 :(得分:2)
在不了解背景的情况下很难回答你的问题。相反,默认情况下,TCP / IP会自动为您处理数据包管理(尽管您以流媒体方式接收它)。但是,当您有一个非常具体(糟糕的?)实现时,您可以同时在1个套接字上发送多个流,从而使较低级别的TCP / IP无法检测到差异。从而使您自己很难在客户端上识别不同的流。对此的唯一解决方案是发送2个完全唯一的流(例如,流1仅发送低于127的字节,而流2仅发送高于或等于127的字节)。再一次,这是一种可怕的行为
答案 3 :(得分:1)
您必须将TCP消息分隔符放入或使用字节计数来指示一条消息的开始位置和另一条消息的开始位置。
您的代码有另一个严重错误。 TCP套接字可能无法在一次调用Receive中为您提供所有数据。您必须循环接收数据,直到数据流中的特定于应用程序的记录指示已收到整个消息。您对client.Receive(data)的调用返回收到的字节数。你应该抓住这个数字。
同样,当您发送数据时,您可能无法在一次通话中发送所有数据。您必须循环发送数据,直到发送的字节数等于您要发送的字节数。对client.Send的调用返回发送的实际字节数,这可能不是您尝试发送的全部内容!
我看到人们使用套接字时最常见的错误是它们不会在发送和接收上循环。如果您了解为什么需要进行循环,那么您就知道为什么需要使用分隔符或字节数。