C# - 使用StreamReader读取HTTP请求

时间:2017-02-25 17:24:52

标签: c# network-programming streamreader tcplistener networkstream

我在C#中编写TCP客户端和服务器,它使用手动编写的HTTP请求相互通信。我遇到的问题是使用Network StreamStreamReader阅读。到目前为止,我尝试了很多方法,但无济于事。

我从TCP客户端获得的请求有多种形式。对于更新数据库,请求看起来像这样( CRLF 是我用来表示"\r\n"字符串的常量):

HTTP 1.0:

  

“POST /”+ name +“HTTP / 1.0”+ CRLF +“Content-Length:”+ 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异常,因为读取时间太长/连接被强行关闭。我关闭了超时,而Read花了无限的时间。

感谢您使用ReadLine(),我能够找到最终挂起所有协议版本的地方,发现当存在两个CRLF集群时("\r\n\r\n") ,Stream Reader无法应对此问题而陷入困境。

关于如何解决这个问题,您有什么建议吗?我需要使用具有多个CRLF的版本,因为它在规范中。

如果您需要任何其他信息,我会尽量以sson的形式提供。

2 个答案:

答案 0 :(得分:2)

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

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读取它时,确实如此。

答案 1 :(得分:1)

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

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

  • Transfer-Encoding: chunked 表示发件人将发送数据块并以0长度块结尾
  • 不使用块时,
  • Content-Length 应该存在,然后您可以准确读取那么多字节的数据
  • GET请求不应该有正文,如果未设置上述标题,您可以认为这是
  • Connection: close 可用于回复,表示在发送所有响应数据后将关闭连接

正如您所看到的,StreamReader.ReadLine()在解析标题时可以很好地工作,它也非常适合读取块,但它不能用于读取固定长度的主体。

我不知道从先前从StreamReader读取的流中读取它是多么可行(它可能会将一些数据提前读取到它的缓冲区),但是打了using块它们只会导致基础流被关闭,除非你pick that one constructor overload