如何使用 Java NIO 限制从 SocketChannel InputStream 一次读取一行
我正在尝试编写一个 Websockets 客户端和服务器。最初连接是 HTTP,Websockets 握手使用 HTTP 标头来指示连接需要升级到新协议。
我想从 SocketChannel 读取 HTTP 标头集,如果指示升级,则切换到不同的库来处理 Websocket,从那时起,以完全不同的方式处理 SocketChannel 流,将其作为一组帧而不是行用 \r\n 分隔。
我知道我可以将任意数量的字节读取到 ByteBuffer 中,但是 Websockets 帧可能已通过握手发送,并且我不想在这些代码部分之间传递半消耗的缓冲区。我想要的是从套接字仅读取直到并包括序列“\r\n\r\n”的数据。除此之外的任何数据我都想留在 SocketChannel 对象输入流中。
推荐的方法是什么?从 SocketChannel 获取输入流并将其包装在缓冲读取器中?这是否可以与 NIO 正确交互,特别是非阻塞使用?一旦检测到空行,我是否可以从输入流中删除缓冲读取器,并且当通道传递到 Websockets 代码时仍然拥有所有可用的帧数据?
或者也许我需要逐字节读取(或者如果某些目标“\r\n\r\n”字符出现在块的末尾,则读取具有较小缓冲区的 4 字节块)并构建我的标头字符串方式。
或者,如果直接分配缓冲区,则操作标记、限制和位置的某种组合可能允许输入流取回之前读入 ByteBuffer 的数据。
任何建议将不胜感激。
I am trying to write a Websockets client and server. Initially the connection is HTTP and the Websockets handshake uses HTTP headers to indicate that an upgrade to a new protocol is necessary on the connection.
I want to read in the set of HTTP headers from the SocketChannel and, if an upgrade is indicated, switch over to a different library for handling Websockets and from that point on handle the SocketChannel streams completely differently, as a set of frames rather than lines delimited with \r\n.
I know I can read an arbitrary number of bytes into a ByteBuffer, but a Websockets frame may have been sent with the handshake and I don't want to be passing off half-consumed buffers between these sections of code. What I want is to read from the socket only the data up to and including sequence "\r\n\r\n". Any data beyond that I want to leave in the SocketChannel object input stream.
What is the recommended way to do this? Get the input stream from the SocketChannel and wrap it in a buffered reader? Would this interact properly with NIO, particularly non-blocking uses? Could I drop the buffered reader from the input stream once the blank line was detected and still have all the frame data available when the channel is passed to the Websockets code?
Or perhaps I need to read byte-by-byte (or 4 byte chunks with smaller buffers if some of the target "\r\n\r\n" characters appear at the end of the chunk) and build up my header strings that way.
Or maybe some combination of manipulating mark, limit and position would allow the input stream to get back data it had previously read into the ByteBuffer provided the buffer was allocated directly.
Any advice would be greatly appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我建议使用 Apache Mina 或 Grizzly 之类的东西。两者都允许您封装问题的协议方面,因此您只需处理可使用的数据。
但是,如果您想要一种快速而肮脏的方式:
但基本思想是,您需要在数据传入时读取数据。如果它不容易使用,我通常会为选择器中的 SelectionKey 创建一些可附加结构(对于简单的 StringBuilder )。每次读取后,我会将数据附加到构建器,如果检测到可用的标头,则将其从缓冲区中切片并将其向上传递(最好在工作线程上)。继续这样做,上游的任何东西都应该能够做出相应的反应。希望有帮助。
所以通常你有这样的结构:
I would recommend using something like Apache Mina or Grizzly. Both allow you to encapsulated the protocol aspect of your problem so you only have to handle consumable data.
However if you want a quick and dirty way:
The basic idea though is yeah you need to read the data as it comes in. If it is not readily usable, I usually create some appendable structure (StringBuilder for something simple) to the SelectionKey that is in the selector. After each read I would append the data to the builder, and if you detect a usable header, slice it out of the buffer and pass it up stream (preferably on a worker thread). Keep doing this and whatever is upstream should be able to react accordingly. Hope that helps.
So usually you have a structure like this:
我将使用适当的面向行的读取器(例如 LineNumberReader)包装套接字 InputStream。在幕后,这些读者一次读取一个字节。由于您所说的原因,我不会为此使用 BufferedReader 。
I would wrap the socket InputStream with an appropriate line oriented reader, such as LineNumberReader. Under the hood these readers read a byte at a time. I would not use a BufferedReader for this, for the reason you state.