正在分析网络流中的数据
本文关键字:数据 网络 | 更新日期: 2023-09-27 18:29:54
最近我开始使用套接字。我意识到,当从网络流中读取时,你无法知道有多少数据进入。因此,要么你提前知道必须接收多少字节,要么你知道哪些字节
由于我目前正在尝试实现一个C#WebSocket服务器,我需要处理HTTP请求。HTTP请求的长度可以是任意的,因此事先知道有多少字节是不可能的。但是HTTP请求总是有特定的格式。它从请求行开始,后面跟着零个或多个头,等等。所以有了所有这些信息,它应该很简单,对吧?
没有。
我想出的一种方法是读取所有数据,直到识别出特定的字节序列。StreamReader类具有ReadLine
方法,我相信它的工作原理是这样的。对于HTTP,合理的分隔符应该是分隔消息头和正文的空行。
这里明显的问题是需要一个(最好是短的)终止序列,比如断线。甚至HTTP规范也表明,这两个相邻的CRLF不是一个好的选择,因为它们也可能出现在消息的开头。毕竟,两个CRLF无论如何都不是一个简单的分隔符。
因此,将该方法扩展到任意类型的3语法,我得出结论,解析数据的最佳选择是有限状态机。我可以一个字节接一个字节地将数据馈送到机器,就像我从网络流中读取数据一样。一旦机器接受输入,我就可以停止读取数据。此外,FSM可以立即捕获重要令牌。
但这真的是最好的解决方案吗?读取一个又一个字节并使用自定义解析器进行验证似乎既乏味又昂贵。FSM要么很慢,要么很难看。所以…
当表单已知但大小未知时,如何处理网络流中的数据
像HttpListener这样的类如何解析消息并快速处理
我错过什么了吗?这通常是怎么做的?
HttpListener
和其他此类组件可以解析消息,因为格式是确定性的。该请求有充分的文件记录。请求标头是一系列以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是一种基于消息的协议;它启用消息流。必须以某种方式界定这些信息;发送方必须告诉接收方消息的末尾在哪里。这可以是隐式的,也可以是显式的。有几种不同的方法:
- 发送方在消息的前几个字节中包含消息的长度。例如,前四个字节是一个二进制整数,表示该消息后面有多少字节。因此,接收器读取前四个字节,将其转换为整数,然后读取那么多字节
- 消息的长度是隐含的。例如,发送方和接收方同意所有消息的长度为80字节
- 消息的第一个字节是消息类型,每个消息类型都有一个定义的长度。例如,消息类型1是40字节,消息类型2是27字节,等等
- 消息有一些终止符。例如,在面向线路的消息系统中,消息由CRLF终止。发件人发送文本,然后发送CRLF。接收器读取字节,直到接收到CRLF
无论何种情况,发送方和接收方必须就消息的结构达成一致。否则,您担心的情况确实会出现:接收器等待永远不会接收到的字节。
为了处理可能的通信问题,您在套接字上设置了ReceiveTimeout属性,这样,如果接收完整消息的时间过长,Read
将抛出SocketException
。这样,您的程序就不会无限期地等待尚未到来的数据。但这种情况只应发生在通信问题的情况下。任何合理的消息格式都将包括一种确定消息长度的方法;要么你知道有多少数据即将到来,要么你知道何时到达消息的末尾。
如果你想发送一条消息,你可以预先挂起消息的大小。获取消息中的字节数,预先挂起一个ulong。在接收器上,读取ulong的大小,解析它,然后从流中读取该字节数,然后关闭它。
在HTTP标头中,您可以读取:Content-Length以八位字节(8位字节)为单位的请求体的长度