简体   繁体   English

C#-使用StreamReader读取HTTP请求

[英]C# - Reading HTTP requests with StreamReader

I am writing a TCP Client and a Server in C# which use manually written HTTP requests to communicate with each other. 我正在用C#编写一个TCP客户端和一个服务器,它们使用手动编写的HTTP请求相互通信。 The trouble I have is with reading from the Network Stream using StreamReader . 我遇到的麻烦是使用StreamReaderNetwork Stream进行读取。 So far, I have tried many methods but to no avail. 到目前为止,我已经尝试了许多方法,但无济于事。

The requests I get from my TCP Client are in in various forms. 我从TCP客户端收到的请求有多种形式。 For updating database, the requests look like this ( CRLF is a constant I use to denote "\\r\\n" string): 对于更新数据库,请求如下所示( CRLF是我用来表示"\\r\\n"字符串的常量):

HTTP 1.0: HTTP 1.0:

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

HTTP 1.1: HTTP 1.1:

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

The requests are of correct form and the client is sending them correctly - I have tested this on a server to which I have access that responded to them without problems. 这些请求的格式正确,客户端正在正确发送它们-我已经在我可以访问的服务器上对该服务器进行了测试,可以对它们进行响应,而不会出现问题。

The problems I have are with my TCP Listener code. 我的问题是我的TCP侦听器代码。 To avoid posting the whole code, I will just include the parts of the code that are problematic (found out by debugging). 为了避免发布整个代码,我将仅包含有问题的代码部分(通过调试找出来)。

Server code: 服务器代码:

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

And the methods used are: 使用的方法是:

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;
}

None of these methods for reading worked. 这些阅读方法均无效。 With my connection timeouts set, I have been getting IO Exception that it took too long to read/the connection was forcibly closed. 设置了我的连接超时后,我收到了IO异常消息,该消息读取时间太长/强行关闭了连接。 I switched off the timeouts and the Read took indefinite amounts of time. 我关闭了超时,“读取”花费了不确定的时间。

Thanks to using ReadLine() s I was able to single out the place where it ultimately hangs for all versions of protocol and found out that when there is cluster of two CRLFs ( "\\r\\n\\r\\n" ), the Stream Reader is not able to cope with this and gets stuck. 多亏使用ReadLine()我能够找出所有协议版本最终挂起的地方,并发现当存在两个CRLF( "\\r\\n\\r\\n" )的簇时,流读者无法对此进行处理并陷入困境。

Do you have any suggestions as on how to get around this? 您对如何解决这个问题有任何建议吗? I need to use the version with multiple CRLFs as it is in the specification. 我需要使用规范中包含多个CRLF的版本。

If you need any additional information, I will try to supply it as sson as possible. 如果您需要任何其他信息,我将尽量提供合理的信息。

In the end I have found a solution to my problem. 最后,我找到了解决问题的方法。 Instead of using 而不是使用

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

I had to use 我不得不用

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

I am still not sure why reading the input by lines did not work but when reading it by char at a time, it does. 我仍然不确定为什么按行读取输入不起作用,但是一次按char读取却可以。

A NetworkStream blocks on a Read operation if there is currently no data available and the other side has not closed that channel yet. 如果当前没有可用数据,并且另一端尚未关闭该通道,则NetworkStream阻止进行Read操作。 TCP by itself has no concept of a message - that problem is to be solved at the HTTP level. TCP本身没有消息的概念-该问题将在HTTP级别解决。

For HTTP you can keep reading until your data contains a \\r\\n\\r\\n sequence, which separates the header from the body. 对于HTTP,您可以继续读取,直到您的数据包含\\r\\n\\r\\n序列为止为止,该序列将标​​头与正文分开。 How to process the body depends on which headers are present: 如何处理正文取决于存在的标头:

  • Transfer-Encoding: chunked indicates that the sender will send chunks of data and will end with a 0-length chunk Transfer-Encoding: chunked表示发件人将发送数据块并将以长度为0的块结尾
  • Content-Length should be present when not using chunks, you can then read exactly that many bytes of data 不使用块时应显示Content-Length ,然后可以准确读取那么多字节的数据
  • GET requests should not have a body, you can probably assume this if the above headers are not set GET请求不应包含主体,如果未设置上述标头,则可以假定此主体
  • Connection: close may be used for responses, indicating that the connection will be closed after all response data is sent Connection: close可用于响应,指示在发送所有响应数据后将关闭连接

As you can see, StreamReader.ReadLine() would work pretty well on parsing the header, and it is quite suitable for reading chunks too, but it cannot be used for reading a fixed-length body. 如您所见, StreamReader.ReadLine()在解析标头时会很好地工作,它也非常适合读取块,但不能用于读取固定长度的正文。

I don't know how realiable it would be to read from a stream previously read from by a StreamReader (it can probably read ahead some data to its buffer), but slapping using blocks around them only causes the underlying stream to be closed unless you pick that one constructor overload . 我不知道从以前由StreamReader读取的流中读取数据(它可以将一些数据提前读取到其缓冲区中)是多么可行,但是using围绕它们的块进行拍打只会导致底层流被关闭,除非您选择一个构造函数重载

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

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