【问题标题】:Socket stream does not contain entire response套接字流不包含整个响应
【发布时间】:2014-07-29 08:31:54
【问题描述】:

我的 c# 应用程序中有一些套接字通信。客户端向服务器发送请求(一个 XML 字符串),服务器以另一个 XML 字符串作为响应。

以下是客户端运行的代码:

using (TcpClient client = new TcpClient(socket.HostName, socket.Port))
{
    client.SendBufferSize = int.MaxValue;
    client.ReceiveBufferSize = int.MaxValue;
    Stream stream = client.GetStream();

    StreamReader sr = new StreamReader(stream);
    StreamWriter sw = new StreamWriter(stream);
    sw.AutoFlush = true;

    sw.WriteLine(requests.ToXML());

    List<string> xmlStrings = new List<string>();
    while (sr.Peek() > -1)
        xmlStrings.Add(sr.ReadLine());
    string xmlStr = string.Join("\r\n", xmlStrings.ToArray());

    ProcessXMLResponse(xmlStr);

    stream.Close();
    client.Close();
}

这在服务器机器上运行:

using (Stream stream = new NetworkStream(socket))
using (StreamReader sr = new StreamReader(stream))
using (StreamWriter sw = new StreamWriter(stream))
{
    sw.AutoFlush = true;

    List<string> xmlStrings = new List<string>();
        while (sr.Peek() > -1)
    xmlStrings.Add(sr.ReadLine());
    string xmlStr = string.Join("\r\n", xmlStrings.ToArray());
    XmlDocument xml = new XmlDocument();
    xml.LoadXml(xmlStr);

    string responseXML = ProcessXMLRequest(xml);

    sw.WriteLine(response);
    socket.Shutdown(SocketShutdown.Both);
    stream.Close();
}

这段代码似乎大部分时间都可以工作,但有时(也许当 XML 响应很大时?),客户端似乎只接收到 XML 回复的第一部分。 sr.Peek() 调用返回 -1,即使数据尚未全部接收。

知道可能是什么问题吗?

【问题讨论】:

  • 可能是因为数据还没有完全发送/接收。尝试提前发送长度,等到socket.Available == length。

标签: c# xml sockets stream


【解决方案1】:

如果你尝试使用会怎样

sr.ReadToEnd()

而不是

sr.ReadLine()

两边?

另外,作为忠告,您应该更加努力地有效地处理您的资源:

using (TcpClient client = new TcpClient(socket.HostName, socket.Port))
{
    client.SendBufferSize = int.MaxValue;
    client.ReceiveBufferSize = int.MaxValue;

    Stream stream = client.GetStream();
    //generally, you should avoid calling Dispose() on an object multiple times

    //don't put this in a using block as that implicitly calls Dispose() which internally calls Dispose on the stream as well
    StreamReader sr = new StreamReader(stream);

    //don't put this in a using block as that implicitly calls Dispose() which internally calls Dispose on the stream as well
    StreamWriter sw = new StreamWriter(stream);

    sw.AutoFlush = true;
    sw.WriteLine("test");

    List<string> xmlStrings = new List<string>();
    while (sr.Peek() > -1)
        xmlStrings.Add(sr.ReadLine());
    string xmlStr = string.Join("\r\n", xmlStrings.ToArray());

    //this calls stream.Dispose as well internally
    sr.Close();

    //actually, this also calls stream.Dispose(), but fortunately MS made it safe
    sw.Close();

    ProcessXMLResponse(xmlStr);
}

//At this point all IDisposable objects have been disposed of properly (TcpClient, Stream, StreamReader, StreamWriter)
//we minimized the number of stream.Dispose() calls whereas using 3 using blocks (1 for the stream, 1 for the Reader and 1 for the Writer) would have resulted in 3 calls

【讨论】:

  • ReadToEnd 似乎不支持这个,它会无限期地阻塞。
【解决方案2】:

我们来看看StreamReader.Peek in MSDN

表示要读取的下一个字符的整数,如果没有要读取的字符或流不支持搜索,则为 -1。

(强调我的)

NetworkStreamis not seekable:

获取一个指示流是否支持搜索的值...该属性总是返回false。

基本上,根据文档,您的代码已损坏;我猜当有现成的数据可用时它正在工作(这将与小型与大型有效负载描述一致),但当数据不能立即可用时会失败。

基本上,不要在这里使用Peek。只需读取数据,直到用完为止。如果您想最小化代码更改:

List<string> xmlStrings = new List<string>();
string line;
while((line = sr.ReadLine()) != null)
    xmlStrings.Add(line);

不过,我个人会以非常不同的方式实现它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多