• 开发进阶系列:Java网络通信编程从基础到框架


    一  基本概念
    1. IO(BIO)和NIO的区别:其本质就是阻塞和非阻塞的区别。
        阻塞:应用程序在获取网络数据的时候,如果网络传输数据很慢,那程序就一直等着,直到传输完毕为止。
        非阻塞:应用程序直接可以获取已经准备就绪的数据,无需等待。
        IO为同步阻塞形式,NIO为同步非阻塞,到JDK1.7,NIO为异步非阻塞。
        同步是指应用程序直接参与IO读写;异步是指操作系统操作IO,应用程序直接取结果。
        同步说的是server服务器端的执行方式; 阻塞说的是具体的技术,接受数据的方式。
    1. NIO基于Buffer(缓冲区)、Channel(管道)、Selector(选择器)。
        2.1 Buffer:实质上是一个数组,它为缓冲区提供了数据操作的概念,如位置、容量等。除Boolean外,每一种java基本类型都对应了一种缓冲区。
        2.2 Channel:类似水管,网络数据通过Channel读取和写入。
          通道是双向的,流是单向的。
          通道可用于读、写或两者同时进行。
          通道可以与多路复用器结合,有多种状态位,方便多路复用器识别。
          通道分为两大类:网络读写(SelectableChannel);
          文件操作(FileChannel);
     
      3. Selector:多路复用器(selector),他是NIO编程的基础,非常重要。多路复用器提供选择已经就绪的任务的能力。

      简单说,就是selector会不断地轮询注册在其上的通道(channel),如果某个通道发生了读写操作,这个通道就处于就绪状态,会被selector轮询出来,然后通过selectionKey可以取得就绪的channel集合,从而进行后续的IO操作。
      一个多路复用器(selector)可以负责成千上万channel通道,没有上限,这也是JDK使用了epoll代替了传统的select实现,获取连接句柄没有限制。这也就意味着我们只要一个线程负责selector的轮询,就可以接入成千上万个客户端,这就是JDK NIO库的巨大进步。

      (扩展——Epoll是什么:epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。)

      selector线程就类似一个管理者(master),管理成千上万个管道,然后轮询哪个管道的数据已经准备好,通知cpu执行IO的读取或写入操作。
    selector模式:当IO事件(管道)注册到选择器以后,selector会分配给每个管道一个key值,相当于标签。selector选择器是以轮询的方式进行查找注册的所有IO事件(管道),当我们的IO事件(管道)准备就绪后,select就会识别,会通过key值来找到相应的管道,进行相关的数据处理操作(从管道里读或写数据,写到我们的数据缓冲区中)。
    每个管道都会对选择器进行注册不同的事件状态,以便选择器查找。

      SelectionKey.OP_CONNECT
    
      SelectionKey.OP_ACCEPT
    
      SelectionKey.OP_READ
    
      SelectionKey.OP_WRITE

     面试常问:Bio——NIO——AIO区别和进化过程

     
    二  Netty
     
    简单强大的NIO框架,异步。可以快速开发网络应用,如服务器和客户端协议。简单实现点我参考
    1、TCP粘包、拆包问题

      熟悉tcp编程的可能都知道,无论是服务器端还是客户端,当我们读取或者发送数据的时候,都需要考虑tcp底层的粘包/拆包机制。
      tcp是一个“流”协议,所谓流就是没有界限的一串数据。大家可以想象下如果河里的水就好比数据,他们是连成一片的,没有分界线。tcp底层并不了解上层的业务数据具体的含义,它会根据tcp缓冲区的实际情况进行包的划分,也就是说,在业务上,我们一个完整的包可能会被tcp分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的tcp粘包、拆包问题。
      分析tcp粘包、拆包问题的产生原因
        1.应用程序write写入的字节大小大于套接口发送缓冲区的大小
        2.进行MSS大小的TCP分段
        3.以太网帧的payload大于MTU进行IP分片

      拆包的三种解决方案
        1.消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格。
        2.在包尾部增加特殊字符进行分割,例如加回车等。
        3.将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑的处理

      拆包具体实现可以用两个类
        1.分隔符类:DelimiterBasedFrameDecoder(自定义分隔符)
        2.FixedLengthFrameDecoder(定长)


    2、Netty编解码技术
          编解码技术,说白了就是Java序列化技术,序列化目的就两个,第一是进行网络传输,第二是对象持久化。
          虽然我们可以使用Java进行对象序列化,netty去传输,但是Java序列化的硬伤太多,比如Java序列化没法跨语言、序列化后码流太大、序列化性能太低等等
          主流的编解码框架:
            JBoss的Marshalling包(是一个Java对象序列化包,对JDK默认的序列化框架进行了优化,又保持了跟java.io.serializable接口的兼容,同时增加了一些可调参数,与netty结合后进行序列化对象的代码编写很简单)
            google的protobuf
            基于protobuf的Kyro
            MessagePack框架

    3、Netty主要应用场景

      1.数据通信  

        两台及以上机器如何使用netty通信:
          1.使用长连接通道不断开的形式进行通信,也就是服务器和客户端的通道一直处于开启状态,如果服务器性能够好,并且客户端数量也比较少的情况下,推荐此方式。
          2.一次性批量提交数据,采用短连接方式。也就是我们会把数据保存在本地临时缓冲区或者临时表里,当达到临界值时进行一次性批量提交,又或者根据定时任务轮询提交,这种情况的弊端是做不到实时传输,对实时性要求不高的应用程序中可以推荐使用。
          3.我们可以使用一种特殊的长连接,在指定某一段时间之内,服务器与某台客户端没有任何通信,则断开连接。下次连接则是客户端向服务器发送请求的时候,再次建立连接。这种模式需要考虑两个因素,
            3.1如何在超时(即服务器和客户端没有任何通信)后关闭通道,关闭后又该如何再次建立连接
            3.2客户端宕机时,我们不用管,下次客户端重启之后我们就可以与服务器建立连接,但当服务器宕机时,我们的客户端如何与服务器进行连接

      2.心跳测试

        我们使用socket通信一般会经常处理多个服务器之间的心跳检测,一般服务器集群,会有一台或多台主机(master),和多台从机(slave),那么主机肯定要时时刻刻知道自己下面的从机的各方面情况,然后进行实时监控的功能,这个在分布式架构里叫做心跳检测或心跳监控。最佳处理方案还是使用一些通信框架进行实现,如netty。

      3.文件上传下载

    附:Mina是一个和netty类似的框架,它属于Apache。

  • 相关阅读:
    Apple Tree(树状数组+线段树)
    平衡阵容(RMQ st表算法)
    一起去打CS
    夜归

    淋雨
    大马戏
    雨中行船
    弄堂里
    夜游天安门
  • 原文地址:https://www.cnblogs.com/zhaot1993/p/13054025.html
Copyright © 2020-2023  润新知