本人对 NIO 的理解程度有限,网上的资料又不够详细,很多东西都搜不到,所以在此问问各位大神,如果兴趣的话还望指教。 谢谢啦。
此时 selector 会把 read 状态的 channel 给获取到 并进入迭代, 这个 channel 里包含本次请求的所有报文吗? 注意是本次请求。
会不会因为网络原因或者其他常见原因 导致数据有断层,比如客户端发送了 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 在收到第二片之后,丢给协议层,这个数据里没有任何特征,协议层是怎么判断出这个报文属于什么协议,从而给对应的协议解析器来进行解析的呢?
我现在是根据报文里的特征来分析的,感觉不太对劲。
![]() | 1 BBCCBB 2021-04-25 14:36:26 +08:00 1. 不一定包含, 大概率都是只有一部分, 有数据就会通知你. 你需要读出来自己缓存起来. 可以看看 Netty 的 ByteToMessageDecoder. 2. 数据包中间穿插了其他的包.. 这个只要在发送的时候中间没穿插其他的包的写入, 一般是不会发生的. 要是会出现这种情况, 那互联网就没法玩了.. |
![]() | 2 BBCCBB 2021-04-25 14:38:38 +08:00 收到的报文是 B 这种.. 你一个完整的包没解析出来的时候, 需要缓存已经读出的数据. 后续的包来的时候, 你需要根据前面缓存的数据来判断属于什么协议.. 还是建议看 Netty. ByteToMessageDecoder 和他的 Codec 那一套东西. |
![]() | 3 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 ? |
4 Jirajine 2021-04-25 14:39:50 +08:00 不了解 NIO,但是 >怎么判断出这个报文属于什么协议 这个需求看起来很奇怪,你要实现 socket mux 么? NIO 是个 IO/网络库吧,正常来说不需要判断啊,数据直接给上层,上层按照自己的协议解析。 如果确实要实现 socket mux,那么应该进行 连接追踪,即请求第一次到达并 dispatch 到相应的后端后记录来源 socket,后续来自相同 socket 的数据 dispatch 到相同的后端。 |
![]() | 5 BBCCBB 2021-04-25 14:40:04 +08:00 说错了, 没主要看你这个分片传输的含义, 失误 分片传输这个看请求了几次... 单次就是 B, 多次的话就类似 A.. |
![]() | 6 BBCCBB 2021-04-25 14:40:57 +08:00 你这分片是说一个文件分多个 chunk 上传的意思? |
7 GuuJiang 2021-04-25 14:43:53 +08:00 恭喜你终于开始有点要逐步转到正确方向的迹象了,我来依次回答下你的每个问题,希望你哪天能正确认识到你在原来那个帖子里犯的错误是多么的基础和低级,并且向所有指出你问题的人一一道个歉 1. 不保证 2. 会 随时记住一点:TCP 是流式协议 关于分片的问题,任何分法都是有可能的,随时记住一点:TCP 是流式协议、TCP 是流式协议、TCP 是流式协议,对上层协议一无所知 关于协议解析的问题,在你选择了监听某个端口之前,这个端口期望收到的协议是已经提前确定了的,换句话说你需要自己负责按照预先的期望去解析协议,并不存在你想象中的“根据收到的内容自动判断协议”这种东西 |
![]() | 8 leopod1995 2021-04-25 14:46:44 +08:00 tcp 分包传输 每个包都是带协议头的 协议层会根据包头来进行数据重组 |
![]() | 9 cheng6563 2021-04-25 14:46:54 +08:00 某些多协议同端口的服务,就是报文里的特征来猜的,没其他办法。 |
10 GuuJiang 2021-04-25 14:54:25 +08:00 我补充一下,确实存在同一个端口同时支持多种协议的情况,但这个和提问者想象的那个不是一回事,可以认为这个分发工作仍然是处在协议处理层,相当于一个复合的协议,具体实现时类似状态机,总之只需要记住一点,IO 部分是不关心上层协议的 |
11 ZhaoHuiLiu 2021-04-25 15:27:21 +08:00 HTTP 是无状态请求的,你两次请求,可能不在一个服务器,你得有一台共用的文件服务器。 分片请求,你得写入一些信息,比如请求的是二进制文件。 你得给这个文件写入一个头部信息,这个头部信息包含:文件名、用户名、文件总大小、已写入大小,请求次数等等固定大小头部信息。 用 fs 打开文件,读取头信息,再写入数据,再更新头信息。 |
12 borisz 2021-04-25 15:28:42 +08:00 多协议同端口,一般会使用一个魔数来区分协议的具体版本. read 如果是标准实现的话, 0 表示正确结束, -1 表示读取到一半出现错误. |
13 ZhaoHuiLiu 2021-04-25 15:28:57 +08:00 HTTP 多次请求,你要完成数据合并,你得写入一个文件,并且这个文件要记录一些头部信息,当请求完成了,你再读取这个文件进行操作。 |
![]() | 14 Joker123456789 OP |
![]() | 15 anexplore 2021-04-25 15:38:51 +08:00 ![]() 1 、并不是;你可能需要接收 N 个 read 事件才能把一个请求的数据都读取完毕 2 、客户端发送的什么你收到的就是什么,不会凭空添加未知数据; 还是用 netty 吧 |
16 dqzcwxb 2021-04-25 17:40:00 +08:00 越看越像是粘包这个老生常谈的引战问题 |
![]() | 17 pabupa 2021-04-25 17:45:10 +08:00 你用了 tcp,就不要考虑这些问题。请您相信它,好吗? |
![]() | 18 monkeyWie 2021-04-25 17:58:16 +08:00 建议手写一个 http paser,这样你应该就懂了 |
![]() | 19 queuey 2021-04-25 18:00:57 +08:00 ![]() 看到 NIO,我以为是蔚来 |
![]() | 21 cqsc 2021-04-25 21:30:18 +08:00 TCP 协议是面向字节流的 同时为了提高效率可能会将多个数据包合并一起发送 或者在接收时多个数据包被拆分成若干小包 这个就需要你自己的在设计应用层协议的时候考虑到如何处理了 确保能够读取到完整的应用层报文 |
![]() | 22 kongch 2021-04-26 10:18:24 +08:00 先从概念看:tcp 是流,属于传输层; http 是应用层,所以答案肯定是 B 。 再用反证法:如果用 A 的模式,你看看你猜测的两个包传输了多少重复内容,计算机先驱们不会有这么低级的错误。 再通过实践:用 wireshark 抓个包就能知道肯定不会是 A 了。 |
23 4kingRAS 2021-04-26 17:30:56 +08:00 协议层不会分“包”的,netty 会设置 delimiter,比如\r\n 。 每次你发的“包”会按照\r\n 来分,所以即使这个请求没有一起到达也没事。 |
24 Chinsung 2021-04-27 09:35:05 +08:00 Netty 中有一个 Http 消息聚合 handler,HttpObjectAggregator,作用就是将过长的 post 请求在多包多次到达的情况将一个长 http 请求聚合成一个完整的请求。 不只是 NIO,其实你这种场景在 BIO 也会出现,本质上是协议解析的问题,网络在发送请求的时候,对于客户端读取方,不论是 BIO 还是 NIO,在 read 的时候都会出现单次 read 只 read 到了一部分报文。 C 里面这种写法会更多,大部分协议或者自定义协议,都一定要有报文头标识和报文结束标识,一般是找几个字节的不可见字符这种,然后自己搞个缓冲区,取出一段一段报文去处理。 |