【问题标题】:How To Signal End Of Data Without Closing NetworkStream in C#如何在 C# 中在不关闭 NetworkStream 的情况下发出数据结束信号
【发布时间】:2011-09-24 09:19:21
【问题描述】:

我有一个将对象序列化并将其发送到服务器应用程序的客户端应用程序。服务器应该反序列化对象,对其进行更改,然后将其序列化并发送回。

服务器代码:

TcpClient client = server.AcceptTcpClient();
using(NetworkStream stream = client.GetStream())
{
    using(StreamReader streamReader = new StreamReader(stream))
    {
       string xmlData = streamReader.ReadToEnd();
    }
}

除非客户端关闭流,否则 ReadToEnd 不会返回。但是如果客户端关闭流,我就无法发送响应。

有没有更好的方法来做到这一点?

【问题讨论】:

  • 顺便提一下,请注意StreamReader.Dispose(在您完成阅读后将被调用)将关闭底层流。无论您的最终解决方案是什么,您都必须避免在发送响应之前关闭StreamReader

标签: c# .net


【解决方案1】:

您可以通过仅关闭一半的双工 TCP 连接来发出“数据结束”的信号。这是通过Socket.Disconnect 完成的。

看看这个例子是如何工作的,我和你的例子很相似。客户端发送数据,然后调用Disconnect;这允许ReadToEnd 返回同时仍保持服务器的一半连接打开。然后服务器发送响应并断开连接,之后双方可以Close结束连接以将其断开。

static void Main(string[] args)
{
    Action clientCode = () =>
        {
            var buffer = new byte[100];
            var clientSocket = new Socket(AddressFamily.InterNetwork,
                                          SocketType.Stream, ProtocolType.Tcp);
            clientSocket.Connect(IPAddress.Loopback, 6690);
            clientSocket.Send(buffer);
            clientSocket.Disconnect(false);
            Console.WriteLine("Client: message sent and socket disconnected.");
            while (true) {
                var bytesRead = clientSocket.Receive(buffer);
                if (bytesRead == 0) {
                    break;
                }

                Console.WriteLine("Client: read " + bytesRead + " bytes.");
            }

            clientSocket.Dispose();
        };

    var server = new TcpListener(IPAddress.Loopback, 6690);
    var thread = new Thread(new ThreadStart(clientCode));
    server.Start();
    thread.Start();
    var client = server.AcceptTcpClient();

    using(NetworkStream stream = client.GetStream()) {
        using(StreamReader streamReader = new StreamReader(stream))
        {
            var data = streamReader.ReadToEnd();
            Console.WriteLine("Server: read " + data.Length + " bytes.");

            // Since we 're here we know that the client has disconnected.
            // Send the response before StreamReader is disposed, because
            // that will cause the socket itself to be closed as well!
            Thread.Sleep(TimeSpan.FromSeconds(1));
            Console.WriteLine("Server: sending response.");
            stream.Write(new byte[10], 0, 10);
            Console.WriteLine("Server: closing socket.");
        }
    }

    server.Stop();
    Console.WriteLine("Server: waiting for client thread to complete.");
    thread.Join();


    return;
}

【讨论】:

  • 我遇到了同样的问题,但我不想从 TcpClient/NetworkStream 切换到直接创建自己的 Socket。显然有一个 TcpClient.Client 套接字,我可以在其上调用 Disconnect()。根据文档,还有一个 NetworkStream.Socket,但在现实生活中它似乎并不存在。希望这行得通。
  • 不,那没用。在实践中,如果我断开连接,则客户端无法从流后记中读取。
  • @skiphoppy 不确定为什么您的尝试不起作用,但是这个确切的代码对我来说很好。也许你做的事情有点不同?
  • 谢谢你,你拯救了我的一天!该断开连接命令确实是发送消息的触发器
【解决方案2】:

您可以使用更高级别的框架,例如 WCF,或者如果您一心想要管理自己的流,则不要使用 ReadToEnd() - 使用 ReadLine()(并让客户端以行的形式发送消息),或使用 Read() 并使用特殊字符(sentinel)表示消息的结束。

【讨论】:

  • 客户端在紧凑框架上运行。不支持 WCF tcp,只支持 http。尝试了 readline,但除非我关闭流,否则它会挂在最后一次调用上。我讨厌添加一个哨兵,因为我正在传输基于 64 位编码的数据,所以两边的代码更多,而且会很棘手。但如果这是唯一的方法,那么这就是我需要知道的。
  • @Ben:如果客户端在写完最后一行后多写一个换行符,它还会挂起吗?
  • 我必须尝试一下才能确定,但​​我敢打赌。 readline 仅在到达流的末尾时才会返回 null。
  • @Ben:我正在考虑使用空行作为标记值。
  • @JimMischel 是的,这行得通。这不是我问题的答案,但这是我将要使用的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多