繁体   English   中英

C#-使用StreamReader读取HTTP请求

[英]C# - Reading HTTP requests with StreamReader

我正在用C#编写一个TCP客户端和一个服务器,它们使用手动编写的HTTP请求相互通信。 我遇到的麻烦是使用StreamReaderNetwork Stream进行读取。 到目前为止,我已经尝试了许多方法,但无济于事。

我从TCP客户端收到的请求有多种形式。 对于更新数据库,请求如下所示( CRLF是我用来表示"\\r\\n"字符串的常量):

HTTP 1.0:

“ POST /” +名称+“ HTTP / 1.0” + CRLF +“ Content-Length:” +长度+ CRLF + CRLF +位置;

HTTP 1.1:

“ POST / HTTP / 1.1” + CRLF +主机名+“ Content-Length:” +长度+ CRLF + CRLF + nameLocString;

这些请求的格式正确,客户端正在正确发送它们-我已经在我可以访问的服务器上对该服务器进行了测试,可以对它们进行响应,而不会出现问题。

我的问题是我的TCP侦听器代码。 为了避免发布整个代码,我将仅包含有问题的代码部分(通过调试找出来)。

服务器代码:

NetworkStream socketStream = new NetworkStream(connection);
StreamReader sr = new StreamReader(socketStream);

string input = ReadAllLinesWithNull(sr); // reading version 1
string input = ReadAllLinesWithEndOfStream(sr);  // reading version 2
string input = ReadAllLinesWithPeek(sr);  // reading version 3
string input = sr.ReadToEnd();  // reading version 4

使用的方法是:

static string ReadAllLinesWithNull(StreamReader sr)
{
    string input;
    string nextLine;
    input = sr.ReadLine();
    while ((nextLine = sr.ReadLine()) != null)
    {
        Console.WriteLine(input);
        input += nextLine;
    }
    sr.Close();
    return input;
}

static string ReadAllLinesWithEndOfStream(StreamReader sr)
{
    string input = "";
    while (!sr.EndOfStream)
    {
        input += sr.ReadLine();
    }
    sr.Close();
    return input;
}

static string ReadAllLinesWithPeek(StreamReader sr)
{
    string input = "";
    while (sr.Peek() >= 0)
    {
        input += sr.ReadLine();
    }
    sr.Close();
    return input;
}

这些阅读方法均无效。 设置了我的连接超时后,我收到了IO异常消息,该消息读取时间太长/强行关闭了连接。 我关闭了超时,“读取”花费了不确定的时间。

多亏使用ReadLine()我能够找出所有协议版本最终挂起的地方,并发现当存在两个CRLF( "\\r\\n\\r\\n" )的簇时,流读者无法对此进行处理并陷入困境。

您对如何解决这个问题有任何建议吗? 我需要使用规范中包含多个CRLF的版本。

如果您需要任何其他信息,我将尽量提供合理的信息。

最后,我找到了解决问题的方法。 而不是使用

static string ReadAllLinesWithPeek(StreamReader sr)
{
    string input = "";
    while (sr.Peek() >= 0)
    {
        input += sr.ReadLine();
    }
    sr.Close();
    return input;
}

我不得不用

static string ReadAllLinesWithPeek(StreamReader sr)
{
    string input = "";
    while (sr.Peek() >= 0)
    {
        input += (char) sr.Read();
    }
    return input;
}

我仍然不确定为什么按行读取输入不起作用,但是一次按char读取却可以。

如果当前没有可用数据,并且另一端尚未关闭该通道,则NetworkStream阻止进行Read操作。 TCP本身没有消息的概念-该问题将在HTTP级别解决。

对于HTTP,您可以继续读取,直到您的数据包含\\r\\n\\r\\n序列为止为止,该序列将标​​头与正文分开。 如何处理正文取决于存在的标头:

  • Transfer-Encoding: chunked表示发件人将发送数据块并将以长度为0的块结尾
  • 不使用块时应显示Content-Length ,然后可以准确读取那么多字节的数据
  • GET请求不应包含主体,如果未设置上述标头,则可以假定此主体
  • Connection: close可用于响应,指示在发送所有响应数据后将关闭连接

如您所见, StreamReader.ReadLine()在解析标头时会很好地工作,它也非常适合读取块,但不能用于读取固定长度的正文。

我不知道从以前由StreamReader读取的流中读取数据(它可以将一些数据提前读取到其缓冲区中)是多么可行,但是using围绕它们的块进行拍打只会导致底层流被关闭,除非您选择一个构造函数重载

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM