问几个有关 NIO 的问题

2021-04-25 14:23:29 +08:00
 Joker123456789

本人对 NIO 的理解程度有限,网上的资料又不够详细,很多东西都搜不到,所以在此问问各位大神,如果兴趣的话还望指教。 谢谢啦。

客户端发起一个 http 请求到服务端

  1. 此时 selector 会把 read 状态的 channel 给获取到 并进入迭代, 这个 channel 里包含本次请求的所有报文吗? 注意是本次请求。

  2. 会不会因为网络原因或者其他常见原因 导致数据有断层,比如客户端发送了 1234,但是 channel 里是 12 34.从而导致读 channel 的时候在断层处 返回 0,而导致读取停止?

分片传输

假如有这样一个 http 报文

POST /demo HTTP/1.1
content-type:application/json
content-length:1000

12345678910qwertyuioasdfghjkl

我以分两片为例子。

分片传输是将报文分为这样的两份( A )?

第一片

POST /demo HTTP/1.1
content-type:application/json
content-length:50

12345678910qwer


第二片
POST /demo HTTP/1.1
content-type:application/json
content-length:50

tyuioasdfghjkl

还是这样的两份( B )?

第一片

POST /demo HTTP/1.1
content-type:application/json
content-length:100

12345678910

第二片
qwertyuioasdfghjkl

如果是 B 的分法,那么 NIO 在收到第二片之后,丢给协议层,这个数据里没有任何特征,协议层是怎么判断出这个报文属于什么协议,从而给对应的协议解析器来进行解析的呢?

NIO 有没有什么高效的办法来 判断本次请求的协议类型,除了分析报文里的特征

我现在是根据报文里的特征来分析的,感觉不太对劲。

3704 次点击
所在节点    Java
24 条回复
BBCCBB
2021-04-25 14:36:26 +08:00
1. 不一定包含, 大概率都是只有一部分, 有数据就会通知你. 你需要读出来自己缓存起来. 可以看看 Netty 的 ByteToMessageDecoder.

2. 数据包中间穿插了其他的包.. 这个只要在发送的时候中间没穿插其他的包的写入, 一般是不会发生的. 要是会出现这种情况, 那互联网就没法玩了..
BBCCBB
2021-04-25 14:38:38 +08:00
收到的报文是 B 这种.. 你一个完整的包没解析出来的时候, 需要缓存已经读出的数据. 后续的包来的时候, 你需要根据前面缓存的数据来判断属于什么协议..

还是建议看 Netty. ByteToMessageDecoder 和他的 Codec 那一套东西.
Ariver
2021-04-25 14:39:04 +08:00
取决于你在哪个层次上写代码。
以下回答认为你是基于 Netty 来实现你的业务逻辑。
1.使用 Netty 提供的 handler 你不需要考虑这个问题,可以认为自己拿到了完整的请求。
2.有可能会发生,那么异常会在你处理这个请求之前就出现,可以认为你的业务代码没有处理。
收到了一部分。
分片
第二种
确认你工作在哪个 level 非常重要。如果你在写网卡驱动,那么你需要确保数据包的收发,如果你在写一个 http server,那么你收到的当然是 http 的请求。监听的端口收到的请求不对是没有办法正确处理的。比如你发一个请求到 22 端口一般来说是收不到返回的。对吧,除非有人部署了一个 http server 在 22 端口。
----
hmmm
所以你要实现的类似于 80 端口有一个 web server 同时想要 ssh ?
Jirajine
2021-04-25 14:39:50 +08:00
不了解 NIO,但是
>怎么判断出这个报文属于什么协议
这个需求看起来很奇怪,你要实现 socket mux 么? NIO 是个 IO/网络库吧,正常来说不需要判断啊,数据直接给上层,上层按照自己的协议解析。

如果确实要实现 socket mux,那么应该进行 连接追踪,即请求第一次到达并 dispatch 到相应的后端后记录来源 socket,后续来自相同 socket 的数据 dispatch 到相同的后端。
BBCCBB
2021-04-25 14:40:04 +08:00
说错了, 没主要看你这个分片传输的含义, 失误

分片传输这个看请求了几次... 单次就是 B, 多次的话就类似 A..
BBCCBB
2021-04-25 14:40:57 +08:00
你这分片是说一个文件分多个 chunk 上传的意思?
GuuJiang
2021-04-25 14:43:53 +08:00
恭喜你终于开始有点要逐步转到正确方向的迹象了,我来依次回答下你的每个问题,希望你哪天能正确认识到你在原来那个帖子里犯的错误是多么的基础和低级,并且向所有指出你问题的人一一道个歉
1. 不保证
2. 会
随时记住一点:TCP 是流式协议

关于分片的问题,任何分法都是有可能的,随时记住一点:TCP 是流式协议、TCP 是流式协议、TCP 是流式协议,对上层协议一无所知

关于协议解析的问题,在你选择了监听某个端口之前,这个端口期望收到的协议是已经提前确定了的,换句话说你需要自己负责按照预先的期望去解析协议,并不存在你想象中的“根据收到的内容自动判断协议”这种东西
leopod1995
2021-04-25 14:46:44 +08:00
tcp 分包传输 每个包都是带协议头的 协议层会根据包头来进行数据重组
cheng6563
2021-04-25 14:46:54 +08:00
某些多协议同端口的服务,就是报文里的特征来猜的,没其他办法。
GuuJiang
2021-04-25 14:54:25 +08:00
我补充一下,确实存在同一个端口同时支持多种协议的情况,但这个和提问者想象的那个不是一回事,可以认为这个分发工作仍然是处在协议处理层,相当于一个复合的协议,具体实现时类似状态机,总之只需要记住一点,IO 部分是不关心上层协议的
ZhaoHuiLiu
2021-04-25 15:27:21 +08:00
HTTP 是无状态请求的,你两次请求,可能不在一个服务器,你得有一台共用的文件服务器。

分片请求,你得写入一些信息,比如请求的是二进制文件。
你得给这个文件写入一个头部信息,这个头部信息包含:文件名、用户名、文件总大小、已写入大小,请求次数等等固定大小头部信息。

用 fs 打开文件,读取头信息,再写入数据,再更新头信息。
borisz
2021-04-25 15:28:42 +08:00
多协议同端口,一般会使用一个魔数来区分协议的具体版本.

read 如果是标准实现的话, 0 表示正确结束, -1 表示读取到一半出现错误.
ZhaoHuiLiu
2021-04-25 15:28:57 +08:00
HTTP 多次请求,你要完成数据合并,你得写入一个文件,并且这个文件要记录一些头部信息,当请求完成了,你再读取这个文件进行操作。
Joker123456789
2021-04-25 15:35:05 +08:00
@BBCCBB
@GuuJiang

好的,非常感谢。 看了你们的回答,让我茅塞顿开。
anexplore
2021-04-25 15:38:51 +08:00
1 、并不是;你可能需要接收 N 个 read 事件才能把一个请求的数据都读取完毕
2 、客户端发送的什么你收到的就是什么,不会凭空添加未知数据;
还是用 netty 吧
dqzcwxb
2021-04-25 17:40:00 +08:00
越看越像是粘包这个老生常谈的引战问题
pabupa
2021-04-25 17:45:10 +08:00
你用了 tcp,就不要考虑这些问题。请您相信它,好吗?
monkeyWie
2021-04-25 17:58:16 +08:00
建议手写一个 http paser,这样你应该就懂了
queuey
2021-04-25 18:00:57 +08:00
看到 NIO,我以为是蔚来
mauve
2021-04-25 18:32:41 +08:00
@queuey 我也

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/773105

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX