本文介绍了EndReceive永远不会返回一个0,你怎么知道什么时候数据包的TCP / IP完成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是类似于我的最后一个问题。我做了一个简单的TCP / IP聊天程序,我有与EndReceive回调函数有点困难。我在微软的执行粘贴(见下文)和我所注意到的是,如果消息中有被读取的全部是不会运行的回调函数,直到被送到下一个消息,即EndReceive绝不会返回一个0。例如如果我送的5个字符的文本消息和缓冲区大小为20,读将在5,我会处理字符串,并调用BeginReceive,但是BeginReceive不运行,直到服务器发送另一个消息。永远达不到其他部分,因为s.EndReceive从未返回0。我怎么会知道,当数据接收完毕?

 公共静态无效Read_Callback(IAsyncResult的AR){
 StateObject所以=(StateObject)ar.AsyncState;
 插座S = so.workSocket; INT读= s.EndReceive(AR); 如果(读大于0){
            so.sb.Append(Encoding.ASCII.GetString(so.buffer,0,读));
            s.BeginReceive(so.buffer,0,StateObject.BUFFER_SIZE,0,
                                  新的AsyncCallback(Async_Send_Receive.Read_Callback),所以);
 }
 其他{
      如果(so.sb.Length→1){
           //所有数据已被读出,因此,将其显示到控制台
           串strContent;
           strContent = so.sb.ToString();
           Console.WriteLine(的String.Format(读取{0}从插座字节+
                           数据= {1},strContent.Length,strContent));
      }
      S.CLOSE();
 }
}


解决方案

EndReceive 很可能得到一个0,如果流的关闭的全部数据已被消耗掉。然而,对于大多数其它用途还有在流没有逻辑突破 - 甚至 NetworkStream.DataAvailable 只是告诉你什么是可在的客户的 - 不是被的服务器的发送。因此:如果你是在同一个流发送多封邮件的/ etc它通常是必要的或者的preFIX预计字节数每个块的的使用一些定界符序列通常不会在内容的中间可以预期的。前者通常更容易,如果内容可以是任意的二进制内容(即它是棘手的predict未预期的序列),但后者允许流的的了解前方的长度时间(如果你是计算上即时的内容,否则,你可能需要缓冲的内容有用的第一个的知道长度(或做一个预演丢弃数据)。

另外请注意,不保证返回所有可用的数据 - 这将是完全合法的发送HELLO,并有只返回1( ^ h )。因此,你应该期望有(使用长度 - preFIX或预期的分隔符序列再次,)将多个结合调用到一条消息。

在这种情况下,我会忍不住发 HELLO \\ 0 (其中 \\ 0 是零-字节)。然后,我将通过读消耗直到或者返回< = 0 ,或 \\ 0 中找到。在 \\ 0 的情况下,任何事物的终结重新$ P $之前缓冲psents一个消息;任何事情的之后的分隔符应存放并作为下一个消息或-消息的一部分进行处理。作为测试的情况下,想象一下你得到返回1​​3,用序列 A \\ 0B \\ 0cde \\ 0F \\ 0 \\ 0 \\0克,它应该bCDE FG(指出先按g 不应该被处理 - 这可能是部分即尚未收到一个较长的序列 - 缓冲区,直到你得到一个 \\ 0

This is similar to my last question. I'm making a simple tcp/ip chat program and I'm having a little difficulty with the EndReceive Callback function. I pasted in Microsoft's implementation (see below) and what I've noticed is that if the message has been read in it's entirety it won't run the callback function until the next message is sent i.e. EndReceive will never return a 0. For example if I send a text message of 5 characters and the buffer size is 20, 'read' will be 5, I'll process the string and call BeginReceive, however BeginReceive doesn't run until the server sends another message. The else section is never reached because s.EndReceive never returns a 0. How would I know when data has been received completely?

public static void Read_Callback(IAsyncResult ar){
 StateObject so = (StateObject) ar.AsyncState;
 Socket s = so.workSocket;

 int read = s.EndReceive(ar);

 if (read > 0) {
            so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
            s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
                                  new AsyncCallback(Async_Send_Receive.Read_Callback), so);
 }
 else{
      if (so.sb.Length > 1) {
           //All of the data has been read, so displays it to the console
           string strContent;
           strContent = so.sb.ToString();
           Console.WriteLine(String.Format("Read {0} byte from socket" +
                           "data = {1} ", strContent.Length, strContent));
      }
      s.Close();
 }
}
解决方案

EndReceive may well get a 0 if the stream is closed and all data has been consumed. However, for most other purposes there is no logical break in a stream - and even NetworkStream.DataAvailable only tells you what is available at the client - not what was sent by the server. Hence: if you are sending multiple messages / etc on the same stream it is usually necessary to either prefix each block with the number of bytes expected, or to use some delimiter sequence that would not normally be expected in the middle of content. The former is often easier if the content could be arbitrary binary content (i.e. it is tricky to predict a sequence that isn't expected), but the latter allows streaming without knowing the lengths ahead of time (useful if you are computing the content on-the-fly, as otherwise you may need to buffer the content first to know the length (or do a dry run discarding the data).

Also note that Read is not guaranteed to return all the available data - it would be entirely legitimate to send "HELLO", and have Read return just 1 (the H). Hence you should expect to have to combine multiple Read calls into a single message (again, using the length-prefix or the expected delimiter sequence).

In this scenario, I'd be tempted to send HELLO\0 (where \0 is a zero-byte). Then I would consume via Read until either it returns <=0, or a \0 is found. In the \0 case, anything buffered before the terminator represents a single message; anything after the delimiter should be stored and processed as part of the next message-or-messages. As a test case, imagine you get Read return 13, with sequence a\0b\0cde\0f\0\0\0g, which which should "a", "b", "cde", "f", "", "", "g" (noting that the g should not be processed - it might be part of a longer sequences that isn't yet received - buffer until you get a \0).

这篇关于EndReceive永远不会返回一个0,你怎么知道什么时候数据包的TCP / IP完成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-29 03:49