为什么服务器在从NetworkStream加载XML时会阻塞?

ski*_*ppy 2 c# xml networking xmldocument networkstream

我在C#中有一个简单的客户端和服务器.目标是客户端将XML文档发送到服务器,然后服务器应该使用不同的XML文档进行响应.当我尝试接收与XMLDocument.load方法(的NetworkStream)输入/请求XML服务器阻塞.

服务器:

        TcpListener t = new TcpListener(IPAddress.Any, 4444);
        t.Start();
        TcpClient c = t.AcceptTcpClient();
        NetworkStream s = c.GetStream();
        XmlDocument req = new XmlDocument();
        req.Load(s);

        string respString = "<response><data>17</data></response>";
        XmlDocument resp = new XmlDocument();
        resp.LoadXml(respString);
        resp.Save(s);
Run Code Online (Sandbox Code Playgroud)

客户:

        TcpClient t = new TcpClient("localhost", 4444);
        NetworkStream s = t.GetStream();

        string reqStr = "<request><parameters><param>7</param><param>15</param></parameters></request>";
        XmlDocument req = new XmlDocument();
        req.LoadXml(reqStr);

        req.Save(s);

        XmlDocument resp = new XmlDocument();
        resp.Load(s);

        Console.WriteLine(resp.OuterXml);
Run Code Online (Sandbox Code Playgroud)

在将请求XmlDocument保存到流中之后,我尝试在客户端中添加Flush(),但这似乎没有帮助.最初我试过让服务器读取从客户端到MemoryStream的所有输入,但后来我发现无法向服务器表明所有输入都是在没有断开连接的情况下完成的,这意味着客户端无法阅读其输入.

我可以使用netcat从文件中将XML输入发送到服务器,一切正常.无论我在服务器中使用XmlDocument.Load(NetworkStream),还是将所有输入读入MemoryStream,这都有效.netcat在这种情况下做的是什么,我没有在我的C#客户端做什么,我怎么用C#做呢?我应该采用不同的方式吗?

Evk*_*Evk 5

Tcp连接是双向的,你可以关闭它的一半,同时仍然打开另一半.在这种情况下,您可以在发送所有数据后关闭客户端到服务器的一半,然后您可以通过服务器接收对客户端的响应一半.你可以像这样做你的例子:

TcpClient t = new TcpClient("localhost", 4444);
NetworkStream s = t.GetStream();
string reqStr = "<request><parameters><param>7</param><param>15</param></parameters></request>";
XmlDocument req = new XmlDocument();
req.LoadXml(reqStr);
req.Save(s);
// important line here! shutdown "send" half of the socket connection.
t.Client.Shutdown(SocketShutdown.Send);
XmlDocument resp = new XmlDocument();
resp.Load(s);
Console.WriteLine(resp.OuterXml);
Run Code Online (Sandbox Code Playgroud)

发送完所有数据后,请不要忘记在服务器端部署网络流:

TcpListener t = new TcpListener(IPAddress.Loopback, 4444);
t.Start();
TcpClient c = t.AcceptTcpClient();
using (NetworkStream s = c.GetStream()) {
    XmlDocument req = new XmlDocument();
    req.Load(s);                
    Console.WriteLine("Got request: {0}", req.OuterXml);
    string respString = "<response><data>17</data></response>";
    XmlDocument resp = new XmlDocument();
    resp.LoadXml(respString);
    resp.Save(s);
}
Run Code Online (Sandbox Code Playgroud)

实际上,如果整个通信是单个请求,然后是单个响应 - 您可以使用此技术而不是自定义协议而不是tcp(请记住使用超时并正确处理您的流和tcp客户端).

否则,请记住网络流是客户端和服务器之间的开放连接 - 它没有明确的"结束".当您执行XmlDocument.Load时 - 它将一直读取直到可能(直到Read返回0字节读取),因此它会阻塞您的情况.您应该通过tcp定义自己的协议,以便您自己定义消息边界.简单的方法是 - 前4个字节定义以下消息的长度.因此,您先读取前4个字节然后读取,直到达到该长度或发生超时.