Java学习总结 1-2-1 Java NIO网络编程

乱七八糟的整理,建议全屏,否则排版可能会很奇怪~~

TCP/UDP协议
    OSI网络七层模型:
        高三层:
            应用层:为用户的应用进程提供网络服务                                   第七层
            表示层:负责数据格式转换、数据加密解密、压缩解压缩等                   第六层
            会话层:负责建立、管理和终止进程之间的会话和数据交换                   第五层
        传输层:提供可靠的端口到端口的数据传输服务(TCP/UDP协议)                  第四层
        低三层:
            网络层:进行路由选择和流量控制(IP协议)                               第三层
            数据链路层:通过检验、确认和反馈重发手段,形成稳定的数据链路(010101) 第二层
            物理层:使原始的数据比特流能在物理上传输                               第一层

    TCP:TCP提供面向连接、可靠、有序、字节流传输服务
        (transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(
         客户端和服务器端)都要有一一成对的socket
        三次握手、四次挥手
        Http协议:
            请求报文格式
                第一部分:请求行   -> 请求类型,资源路径以及HTTP版本   例:GET /chen.do HTTP/1.1
                第二部分:请求头部 -> 接着请求行(第一行)之后部分,用来说明服务器要使用的附加信息
                第三部分:空行     -> 请求头后面的空行是必须的,请求头部和数据主体之间必须有换行
                第四部分:请求的数据也叫主体,可以添加任意的数据。

            返回报文格式
                第一部分:状态行 -> HTTP版本、状态码、状态消息       例:HTTP/1.1 200 OK
                第二部分:响应报文头部 -> 接着响应行(第一行)之后的部分,用来说明服务器要使用的附加信息
                第三部分:空行 -> 响应头后面的空行是必须的,请求头部和数据主体之间必须有换行
                第四部分:响应正文。可以添加任意数据

            响应状态码(HTTP协议)
                1XX:临时响应  -> 表示临时响应并需要请求者继续执行的状态码
                2XX;成功      -> 成功处理了请求的状态码 
                3XX;重定向    -> 表示完成请求,需要进一步操作。通常这些状态码用来重定向
                4XX:请求错误   -> 表示请求可能出错,妨碍了服务器的处理
                5XX:服务器错误 -> 表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错

    UDP:用户数据协议UDP是Internet传输层协议。提供无连接、不可靠、数据包尽力传输服务
    UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,
    由于UDP支持的是一对多的模式
        相比TCP:
            1.无需建立连接
            2.无连接状态
            3.首部开销小

    TCP/UDP:
            TCP            UDP
          面向连接        无连接
          提供可靠性保障  不可靠
             慢             快
          资源占用多      资源占用少

    Socket编程:Internet中应用最广泛的网络应用编程接口,实现于三种底层协议接口:
        1.数据包类型套接字SOCK_DGRAM (面向UDP接口)
        2.流式套接字SOCK_STREAM      (面向TCP接口)
        3.原始套接字SOCK_RAW         (面向网络层协议接口IP、ICMP等)

        主要socket API及调用过程:
            创建套接字 -> 端点绑定 -> 发送数据 -> 接收数据 -> 释放套接字


 BIO网络编程
    
    阻塞IO:
        阻塞(blocking)IO:资源不可用时,IO请求一直阻塞,直到反馈结果(有数据或超时)
        非阻塞(non-blocking)IO:资源不可用时,IO请求离开返回,返回数据标识资源不可用

        同步(synchronous)IO:应用阻塞在发送或接收数据的状态,直到数据成功传输或返回失败
        异步:(asynchronous)IO:应用发送或接收数据后立即返回,实际处理时异步执行的

        阻塞导致在处理网络I/O时,一个线程只能处理一个网络连接
        注:阻塞和非阻塞是获取资源的方式。同步和异步是程序如何处理资源的逻辑设计。ServerSocket#accept、InputStream#read都是阻塞API。操作系统底层API中,默认Socket操作都是Blocking型(阻塞),send/recv等接口都是阻塞的。

    API
        服务端:ServerSocket:
            listern(); accept(); 

        客户端:Socket:
            send();


NIO非阻塞网络编程(采用多路复用IO/事件驱动IO模型) https://www.cnblogs.com/lighten/p/8922186.html
        三个核心组件:
            Buffer:缓冲区 -> 与流式的 I/O 不同,NIO是基于块(Block)的,它以块为基本单位处理数据。缓冲是一块连续的内存块,是 NIO
                    读写数据的中转地。通道表示缓冲数据的源头或者目的地,它用于向缓冲读取或者写入数据,是访问缓冲的接口。相比较
                    直接对数组的操作,Buffer API更加容易操作和管理   示例:

Java学习总结 1-2-1 Java NIO网络编程_第1张图片

                    三个重要属性:
                        1.capacity容量:作为一个内存块,Buffer具有一定的固定大小,也成为“容量”
                        2.position位置:写入模式时代表写数据的位置。读取模式时代表读取数据的位置
                        3.limit限制:   写入模式,限制等于buffer的容量。读取模式下,limit等于写入的数据量


                ByteBuffer内存模型:
                    直接内存(direct堆外内存):在JVM之外向物理内存直接申请内存
                        ByteBuffrt directByteBuffer = ByteBuffer.allocateDirect(noBNytes);
                        优点:
                            1、进行IO或文件IO时比heapBuffer少一次拷贝。(file/socket -> OS memory -> jvm heap)
                            2、在GC范围之外,降低GC压力,但实现了自动管理。DirextByteBuffer类提供Cleaner对象(PhantomReference),Cleaner被GC前会执行clean方法,触发DirectByteBuffer中定义的Deallocator
                    
                    非直接内存(heap堆内存):在JVM的申请内存

                    建议:性能确实可观的时候才去使用;分配给大型、长寿命;(网络传输、文件读写场景)
                        通过虚拟机参数MaxxDirectMemorySize限制大小,防止耗尽整个机器内存

            Channel: 通道
                代码 -> buffer -> channel

                和标准的IO Stream区别:在一个通道内进行读取和写入时stream通常是单向的(input和output),可以非阻塞读取和写入通道,通道始终读取或写入缓冲区,channel的API涵盖了UDP/TCP网络和文件IO。(FileCjannel,DatagramCjannel,SocketChannel,ServerSocketChannel)

                SocketChannel:用于创建TCP连接,类似java.net.Socket.有两种创建的方式:
                    1、客户端主动发起和服务的连接。
                    2、服务端获取新的连接

                    write写:write()在尚未写入任何内容可能就返回了。需要在循环中调用
                    read读:read()方法可能直接返回而根本不读取任何数据,根据返回的int值判断读取了多少字节

                    SockerChannel:客户端
                        //客户端主动发起连接的方式
                        SocketChannel socketChannel = SocketChannel.open();
                        //设置为非阻塞模式
                        socketChannel.configureBlocking(false);
                        //创建一个端口和ip
                        socketChannel.connect(new InetSocketAddress("htttp:163.com",8080));
                        //发送请求数据 - 向通道写入数据
                        socketChannel.write(byteBuffer);
                        //读取服务端返回 - 读取缓冲区的数据
                        int butesRead = socketChannel.read(byteBuffer);
                        //关闭连接
                        socketChannel.close();

                    ServerSocketChannel:服务端
                        //创建网络服务端
                        ServerSocketChannel serverSockerChannel = ServerSocketChannel.open();
                        //设为非阻塞模式
                        serverSocletChannel.configureBlocking(false);
                        //绑定端口
                        serverSocletChannel.socket().bind(new InetSocketAddress(8080));
                        while(true){
                            SocketChannel soccketChannel = serverSocletChannel.accept();
                            if(socketChannel != null){
                                //TCP请求 读取/响应
                            }
                        }
                        注:serverSocletChannel.accept()如果该通道处于非阻塞,如果没有挂起的连接则该方法立即返回null。
                            所以必须检查返回的对象是否为空

            Selector:选择器
                Thread -> Selector -> 多个channel(serverSocker socket) -> ByteBuffer
                Selector:          选择器
                    四种事件:
                        Connect:连接(Selector.OP_CONNECT)
                                    客户端与服务端建立连接,客户端触发事件
                        Accept: 准备就绪(OP_ACCEPT)
                                    服务端检测到客户端连接请求,服务端触发事件
                        Read:   读取(OP_READ)
                                    客户端与服务端建立连接之后读取操作,双端触发事件
                        Writ:   写入(OP_WRITE)
                                    客户端与服务端建立连接之后写入操作,双端触发事件
                SelectableChannel: 可选择通道
                SelectionKey:      选择键
                
                服务端: 
                    对于服务端而言首先需要的就是确定监听的端口,其次是与之对应的channel,而后就是selector,最后还需要一个线程池。为什么会需
                    要线程池呢?道理很简单,select模式获取了所有channel的change,对于服务端而言,change的可能有非常多的客户端channel,而用
                    户程序只有一个线程,如果这么多个channel一个个顺序执行,如果有耗时严重的操作,那么后果是非常糟糕的,所有客户端都会延时
                    处理,这也是多路IO复用的一个糟糕点。线程池就是为每个客户端分配一个线程去处理,减缓这种情况的后果。Server的基本四个内容
                    就出来了:
                        private int port;
                        private Selector selector;
                        private ServerSocketChannel serverSocketChannel;
                        private ExecutorService executorService;
                    接下来就是初始化服务端。初始化的步骤也是一般化:1.初始化连接池;2.初始化Selector;3.初始化绑定端口;4.将socket注
                    册到select上。大致步骤就是这些,但是还有些额外的细节。具体代码如下:
                        executorService = Executors.newCachedThreadPool();
                        selector = Selector.open();
                        serverSocketChannel = ServerSocketChannel.open();
                            serverSocketChannel.configureBlocking(false);
                            serverSocketChannel.socket().bind(new InetSocketAddress(port));
                        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        selector()方法:
            NIO使用的IO原型是多路复用IO模型,也叫事件驱动IO,这种IO模型采用select/epoll(IO多路复用,复用同一个线程)模式,
            select会轮询查询所有绑定在自身上的Channel,当有某个Channel数据到达了,就会通知进程(Channel数据到达意思是绑定在
            Channel上的socket产生了链接)。select()方法就是Selector通知进程之前使当前的代码段进入阻塞状态,说白了就是
            在while(true)里阻塞,不消耗cpu而已
        
        发送流程:
            不管是客户端发送服务端接收,还是服务端发送客户端接收,基本的流程都是:发送方发送数据->buffer->发送方channel->接收方channel->buffer->接收方接收数据
        
        阻塞和非阻塞IO对比:    
            当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中
            的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。这个图和blocking IO
            的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而blocking IO只调用
            了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。    
                
    总结:
        1. 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性
            能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
        2. 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直
            被block的。只不过process是被select这个函数block,而不是被socket IO给block。
        结论: select的优势在于可以处理多个连接,不适用于单个连接 

                
                

    


 

你可能感兴趣的:(博客作业,班级作业,网易云专业-微专业Java)