从网络流中解析数据?

时间:2013-09-01 23:50:43

标签: c# parsing http networkstream

最近我开始使用套接字。我意识到,当从网络流中读取时,您无法知道有多少数据正在进入。所以要么您事先知道需要多少字节,要么您知道哪些字节< /强>

由于我目前正在尝试实现C#WebSocket服务器,因此我需要处理HTTP请求。 HTTP请求可以具有任意长度,因此事先知道多少字节是不可能的。但HTTP请求始终具有certain format。它从请求行开始,后跟零个或多个标题等。所以这些信息应该很简单,对吧?

不。

我提出的一种方法是读取所有数据,直到识别出特定的字节序列。 StreamReader classReadLine方法,我相信这样做的方法就是这样。对于HTTP,合理的分隔符将是空行将消息头与正文分开。

这里显而易见的问题是需要一个(最好是短的)终止序列,比如换行符。甚至HTTP规范也表明这两个相邻的CRLF不是一个好的选择,因为它们也可能出现在消息的开头。毕竟,无论如何,两个CRLF都不是简单的分隔符。

因此,将方法扩展为任意类型3语法,我总结出解析数据的最佳选择是有限状态机。我可以在数据字节之后将数据提供给机器,就像我从网络流中读取数据一样。一旦机器接受输入,我就可以停止读取数据。此外,FSM可以立即捕获重要的令牌。

但这真的是最好的解决方案吗?逐字节读取并使用自定义解析器验证它看起来既乏味又昂贵。 FSM可能会很慢或很难看。所以......

当表单已知但不是大小时,如何处理来自网络流的数据?

HttpListener这样的课程如何解析邮件并快速处理?

我在这里错过了什么吗?通常如何做到这一点?

2 个答案:

答案 0 :(得分:3)

HttpListener和其他此类组件可以解析消息,因为格式是确定性的。请求is well documented。请求标头是一系列CRLF终止的行,后跟一个空行(连续两个CRLF)。

消息正文可能难以解析,但它的确定性在于标题会告诉您使用了什么编码,是否已经压缩等等。即使是多部分消息也不是很难解析。

是的,您需要一台状态机来解析HTTP消息。是的,你必须逐字节解析它。它有点牵扯,但速度非常快。通常,您将流中的一堆数据读入缓冲区,然后逐字节处理该缓冲区。您不会一次读取一个字节的流,因为开销会导致性能下降。

您应该查看HttpListener源代码,看看它是如何工作的。转到http://referencesource.microsoft.com/netframework.aspx并下载.NET 4.5 Update 1源。

准备花费大量时间深入研究并通过HTTP规范。

顺便说一下,创建一个处理一小部分HTTP请求的程序并不困难。但我想知道为什么当你只能使用HttpListener并为你处理所有细节时,你想要这样做。

更新

您正在谈论两种不同的协议。 HTTP和WebSocket是两个完全不同的东西。正如维基百科的文章所说:

  

WebSocket协议是一种独立的基于TCP的协议。它与HTTP的唯一关系是它的握手被HTTP服务器解释为升级请求。

使用HTTP,您知道服务器将发送流然后关闭连接;它是一个带有定义结束的字节流。 WebSocket是一种基于消息的协议;它启用了一组消息。必须以某种方式描述这些信息;发送方必须告诉接收方消息的结尾。这可以是隐含的或明确的。有几种不同的方法:

  1. 发件人在邮件的前几个字节中包含邮件的长度。例如,前四个字节是一个二进制整数,表示该消息中跟随的字节数。所以接收器读取前四个字节,将其转换为整数,然后读取那么多字节。
  2. 消息的长度是隐含的。例如,发送方和接收方同意所有消息都是80字节长。
  3. 消息的第一个字节是消息类型,每个消息类型都有一个已定义的长度。例如,消息类型1是40个字节,消息类型2是27个字节,等等。
  4. 消息有一些终止符。例如,在面向行的消息系统中,消息由CRLF终止。发件人发送文本,然后发送CRLF。接收器读取字节,直到收到CRLF。
  5. 无论如何,发件人和收件人必须就如何构建邮件达成一致。否则你担心的情况会突然出现:接收器等待永远不会收到的字节。

    为了处理可能的通信问题,您在套接字上设置ReceiveTimeout属性,以便Read如果收到完整的消息需要很长时间,则会抛出SocketException。这样,您的程序将无法无限期地等待即将到来的数据。但这应该只发生在通信问题上。任何合理的消息格式都将包括确定消息长度的方法;要么你知道有多少数据,要么你知道什么时候你已经到达消息的末尾。

答案 1 :(得分:0)

如果您想发送消息,可以将消息的大小预先添加到消息中。获取消息中的字节数,预先将ulong挂起。在接收器,读取ulong的大小,解析它,然后从流中读取该字节数,然后关闭它。

在HTTP标头中,您可以读取:Content-Length请求正文的长度,以八位字节为单位(8位字节)