V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Joker123456789
V2EX  ›  Java

问几个有关 NIO 的问题

  •  
  •   Joker123456789 · 2021-04-25 14:23:29 +08:00 · 3913 次点击
    这是一个创建于 1305 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本人对 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 有没有什么高效的办法来 判断本次请求的协议类型,除了分析报文里的特征

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

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

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

    还是建议看 Netty. ByteToMessageDecoder 和他的 Codec 那一套东西.
    Ariver
        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 ?
    Jirajine
        4
    Jirajine  
       2021-04-25 14:39:50 +08:00
    不了解 NIO,但是
    >怎么判断出这个报文属于什么协议
    这个需求看起来很奇怪,你要实现 socket mux 么? NIO 是个 IO/网络库吧,正常来说不需要判断啊,数据直接给上层,上层按照自己的协议解析。

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

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

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

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

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

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

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

    好的,非常感谢。 看了你们的回答,让我茅塞顿开。
    anexplore
        15
    anexplore  
       2021-04-25 15:38:51 +08:00   ❤️ 1
    1 、并不是;你可能需要接收 N 个 read 事件才能把一个请求的数据都读取完毕
    2 、客户端发送的什么你收到的就是什么,不会凭空添加未知数据;
    还是用 netty 吧
    dqzcwxb
        16
    dqzcwxb  
       2021-04-25 17:40:00 +08:00
    越看越像是粘包这个老生常谈的引战问题
    pabupa
        17
    pabupa  
       2021-04-25 17:45:10 +08:00
    你用了 tcp,就不要考虑这些问题。请您相信它,好吗?
    monkeyWie
        18
    monkeyWie  
       2021-04-25 17:58:16 +08:00
    建议手写一个 http paser,这样你应该就懂了
    queuey
        19
    queuey  
       2021-04-25 18:00:57 +08:00   ❤️ 4
    看到 NIO,我以为是蔚来
    mauve
        20
    mauve  
       2021-04-25 18:32:41 +08:00
    @queuey 我也
    cqsc
        21
    cqsc  
       2021-04-25 21:30:18 +08:00
    TCP 协议是面向字节流的 同时为了提高效率可能会将多个数据包合并一起发送 或者在接收时多个数据包被拆分成若干小包 这个就需要你自己的在设计应用层协议的时候考虑到如何处理了 确保能够读取到完整的应用层报文
    kongch
        22
    kongch  
       2021-04-26 10:18:24 +08:00
    先从概念看:tcp 是流,属于传输层; http 是应用层,所以答案肯定是 B 。
    再用反证法:如果用 A 的模式,你看看你猜测的两个包传输了多少重复内容,计算机先驱们不会有这么低级的错误。
    再通过实践:用 wireshark 抓个包就能知道肯定不会是 A 了。
    4kingRAS
        23
    4kingRAS  
       2021-04-26 17:30:56 +08:00
    协议层不会分“包”的,netty 会设置 delimiter,比如\r\n 。 每次你发的“包”会按照\r\n 来分,所以即使这个请求没有一起到达也没事。
    Chinsung
        24
    Chinsung  
       2021-04-27 09:35:05 +08:00
    Netty 中有一个 Http 消息聚合 handler,HttpObjectAggregator,作用就是将过长的 post 请求在多包多次到达的情况将一个长 http 请求聚合成一个完整的请求。
    不只是 NIO,其实你这种场景在 BIO 也会出现,本质上是协议解析的问题,网络在发送请求的时候,对于客户端读取方,不论是 BIO 还是 NIO,在 read 的时候都会出现单次 read 只 read 到了一部分报文。
    C 里面这种写法会更多,大部分协议或者自定义协议,都一定要有报文头标识和报文结束标识,一般是找几个字节的不可见字符这种,然后自己搞个缓冲区,取出一段一段报文去处理。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3011 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 14:42 · PVG 22:42 · LAX 06:42 · JFK 09:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.