我在C#中编写TCP客户端和服务器,它使用手动编写的HTTP请求相互通信。我遇到的问题是使用Network Stream
从StreamReader
阅读。到目前为止,我尝试了很多方法,但无济于事。
我从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的形式提供。
答案 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
应该存在,然后您可以准确读取那么多字节的数据Connection: close
可用于回复,表示在发送所有响应数据后将关闭连接正如您所看到的,StreamReader.ReadLine()
在解析标题时可以很好地工作,它也非常适合读取块,但它不能用于读取固定长度的主体。
我不知道从先前从StreamReader
读取的流中读取它是多么可行(它可能会将一些数据提前读取到它的缓冲区),但是打了using
块它们只会导致基础流被关闭,除非你pick that one constructor overload。