我想使用 NetworkStream (或者可能是 Socket )来读/写 TCP 连接。我想使用非阻塞操作,这样我就不必处理多个线程,或者处理如果某些线程可能因阻塞网络操作而停止时如何停止程序的问题。

documentation of NetworkStream.Read 意味着它是非阻塞的(“如果没有数据可供读取,则 Read 方法返回 0”),所以我想我不需要异步回调来读取......对吗?

但是写作呢?好吧,微软在这个主题上有一个通常的低质量 tutorials,他们建议为每个操作编写一个繁琐的 AsyncCallback 方法。这个回调中的代码有什么意义?

client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None,
                 new AsyncCallback(SendCallback), client);
    ...
private static void SendCallback(IAsyncResult ar)
{
    try {
        Socket client = (Socket)ar.AsyncState;

        int bytesSent = client.EndSend(ar);

        Console.WriteLine("Sent {0} bytes to server.", bytesSent);
        // Signal that all bytes have been sent.

        sendDone.Set();
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
}
AsyncCallback 的文档说它是“异步操作完成时调用的回调方法”。如果操作已经完成,为什么还要调用 Socket.EndSend(IAsyncResult) 呢?同时, NetworkStream.BeginWrite 的文档说“您必须创建一个实现 AsyncCallback 委托(delegate)的回调方法并将其名称传递给 BeginWrite 方法。至少,您的状态参数必须包含 NetworkStream 。”

但为什么“必须”我?我不能把 IAsyncResult 存储在某个地方吗...
_sendOp = client.BeginSend(message, 0, message.Length, SocketFlags.None, null, null);

...并定期检查它是否完成?
if (_sendOp.IsCompleted)
    // start sending the next message in the queue

(我知道这意味着轮询,但它将位于预计大部分时间都处于事件状态的服务器中,我计划在空闲时使用一些 Thread.Sleeps。)

另一个问题。我发送的消息被分解为一个标题数组和一个正文数组。我可以连续发出两个 BeginWrites 吗?
IAsyncResult ar1 = _stream.BeginWrite(header, 0, header.Length, null, null);
IAsyncResult ar2 = _stream.BeginWrite(msgBody, 0, msgBody.Length, null, null);

另外,对于是使用 Socket 还是 NetworkStream,欢迎提出任何意见。

最佳答案

我对 NetworkStream 没有任何经验,但我可以这样说异步 Socket 操作:

  • End* 需要在操作完成时调用,因为这会清理异步操作使用的操作系统资源并允许您获取操作的结果(即使该结果只是成功或失败)。 MSDN 有 a good overview 这种常见的异步模式。
  • 您不需要将任何特定对象作为状态参数传递。在编写该文档时,传递它是将对象保持在完成委托(delegate)范围内的最简单方法。如今,对于 lambda 表达式,这种做法并不常见。
  • 您可以对套接字的写入进行排队,并且在大多数情况下这将起作用。但是,其中一个写入可能只是部分完成(我从未见过这种情况发生,但理论上是可能的)。我这样做了多年,但不再推荐它。

  • 我确实建议使用完成委托(delegate)而不是轮询。如果您想要一个更易于使用的包装器,您可以尝试 Nito.Async.Sockets ,它包装 Begin/End 操作并通过单个线程(例如,UI 线程)将它们全部序列化。

    附言如果要停止阻塞操作,可以从另一个线程关闭套接字,这将导致操作完成并出现错误。但是,我不建议使用阻塞套接字操作。

    关于c# - 如何正确异步编写 Socket/NetworkStream?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5797477/

    10-14 11:37