如何确定异步套接字服务器中流的结束

3
我可以为您翻译文本。这篇文章涉及到C#异步套接字编程,作者参考了两个MSDN示例:服务器端客户端。作者通过理解示例代码并使用其实现了自己的异步套接字。在示例中,服务器会检查是否出现了<EOF>来确定数据流的结束并发送响应。作者想知道如何在不检查特殊文字的情况下确定数据流的结束。作者的想法是当bytesRead > 0时调用handler.BeginReceive()进行递归操作。具体内容请见以下:
if (bytesRead > 0) {
    // There  might be more data, so store the data received so far.
    state.sb.Append(Encoding.ASCII.GetString(
        state.buffer,0,bytesRead));

    // Check for end-of-file tag. If it is not there, read 
    // more data.
    content = state.sb.ToString();
    if (content.IndexOf("<EOF>") > -1) {
        // All the data has been read from the 
        // client. Display it on the console.
        Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
            content.Length, content );
        // Echo the data back to the client.
        Send(handler, content);
    } else {
        // Not all data received. Get more.
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
        new AsyncCallback(ReadCallback), state);
    }
}

My idea

if (received > 0)
{
    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, received));
    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
}
else
    Send(handler, state.sb.ToString());

如果我用我的部分替换原始部分,程序就会停止运行。 我猜测客户端在SendCallback()中执行EndSend(),并在StartClient()中的receiveDone.WaitOne()之后阻塞线程。我该怎么办?需要使用一个标记来确定流的结尾吗?
还有其他好的示例代码吗?我只找到了这两个。
(证据。服务器应接收在线客户端并将它们放入循环缓冲区,从该缓冲区中读取和处理记录,并使用多个线程进行处理。) 编辑 如果我使用以下内容:
if (receive > 0)
    state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, receive));
if (receive == StateObject.BufferSize)
    state.listener.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
else
    Send(state.listener, state.sb.ToString());

一切看起来都很好,我猜。没问题吧?还是我漏了什么?

如果我将两个if组合起来,它又不起作用了。为什么?

if(receive > 0 || receive == StateObject.BufferSize) -> if(receive > 0) // 不起作用。

2个回答

4
你的代码版本存在问题,就是你总是在接收到0个字节时才启动BeginReceive。实际上,大多数情况下,传输的结束都会发生在接收到一个非零且小于缓冲区大小的数据量时(有时恰好等于缓冲区大小)。接收到0字节永远不会发生,因为这意味着没有传输而不是传输结束,这是假设发送者通过关闭连接来结束传输的情况。
有几种方法可以表示传输结束:
  • 发送方关闭连接
  • 发送特定的序列(如EOF)
  • 接收方得到了它预期的数据(因为它首先收到头文件)
第一种方法的问题在于关闭(和重新打开)连接通常是昂贵的。使用头文件的问题是,如果错过头文件(因为某些网络问题),您就会丢失 - 您不知道下一个头文件何时到达,因为您不知道当前数据包何时结束(丢失头文件)。在出现问题的情况下,最简单的恢复方法是以某种形式使用EOF。它不一定要文字上是<EOF>字符串,可以是任何在正常数据流中(最可能)从未出现的内容。
话虽如此,你可以用一个头文件与EOF结合使用,或者一个包含记录开头特定序列的头文件。

0
当远程端进行连接优雅关闭时,您可以确定网络流已经结束。这会导致 EndRead 返回零。当然,如果您想将数据发送回远程端,则这并不是最佳方法。您实际上需要创建一个新的连接返回给客户端,而不是重用初始连接。这当然是可行的,但通常情况下效果不佳,例如当客户端在 NAT 路由器后面时。
当样例代码检查 EOF 时,它强制执行应用层协议。底层网络堆栈无法理解其传递给应用程序的有效载荷,因此无法确定概念流是否已结束。这是您的应用程序有责任的事情。检查标记或令牌是一种非常简单的方法。

使用令牌是一种常见的方式吗?我想先发送内容的长度,这可行吗?如果接收到的数据等于缓冲区大小,那么我可以假设还有更多的数据吗? - Andre Hofmeister
通常使用某种分隔符,但在发送数据之前发送长度(或更常见的八位数)也经常出现。只要您有一个双方都同意的协议,就可以继续进行。 - user858224

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接