【问题标题】:How to determine stream end in a asynchronous socket server如何确定异步套接字服务器中的流结束
【发布时间】:2013-04-03 15:36:35
【问题描述】:

我正在尝试用 C# 编写一个异步套接字。我阅读了很多 msdn 文章并找到了这两个示例:serverclient

我理解了示例代码并使用它来实现我自己的异步套接字。在示例中,服务器检查<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);
    }
}

我的想法

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 &gt; 0 || receive == StateObject.BufferSize) -> if(receive &gt; 0) // Not working.

【问题讨论】:

    标签: c# sockets asynchronous stream


    【解决方案1】:

    你的代码版本的问题是你总是启动BeginReceive,但在一种情况下,即当收到0字节时。事实上,当您收到非零且小于缓冲区大小的数据量(有时恰好是缓冲区大小时)时,大多数情况下会发生传输结束。接收0 字节将永远不会发生,因为这意味着没有传输,而不是传输结束。那是假设发送方通过关闭连接来结束传输。

    有多种方式表示传输结束:

    • 发件人关闭连接
    • 发送特殊序列(如 EOF)
    • 接收者得到了他所期望的(因为它首先得到了标题)

    第一种方法的问题是关闭(和重新打开)连接通常很昂贵。 标头的问题是,如果您错过了一个标头(由于某些网络问题),您将丢失 - 您不知道下一个标头何时到来,因为您不知道当前数据包何时结束(丢失标头)。出现问题时最容易的恢复以某种形式提供 EOF。它不必是字面上的&lt;EOF&gt; 字符串,它可以是(很可能)不会在您的正常数据流中出现的任何内容。

    话虽如此,没有什么能阻止您将标头与EOF 或包含特殊序列标记记录开头的标头组合使用。

    【讨论】:

      【解决方案2】:

      您可以确定网络流已经结束的情况是远程端正常关闭连接。这将使EndRead 返回零。如果您想将数据发送回远程端,这当然不是最佳方法。您基本上必须创建一个返回客户端的新连接,而不是重用初始连接。这当然是您可以做的,但它通常不会很好地工作,例如,如果客户端位于 NAT 路由器后面。

      示例代码在检查EOF 时执行的操作是执行应用程序级协议。底层网络堆栈不了解它传递给您的应用程序的有效负载,因此无法确定概念流已经结束。这是您的应用程序有责任的事情。检查标签或令牌是一种非常简单的方法。

      【讨论】:

      • 使用token是常用的方式吗?我想先发送内容的长度,可以吗?如果收到的缓冲区大小等于缓冲区大小,我可以假设还有更多数据?
      • 使用某种分隔符是很常见的,但在发送数据之前发送长度(或更常见的是八位字节数)也经常发生。只要您有一个两个端点都可以同意的协议,您就可以开始了。
      猜你喜欢
      • 2017-07-03
      • 2011-07-13
      • 1970-01-01
      • 2020-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-27
      • 1970-01-01
      相关资源
      最近更新 更多