socket网络编程-基础篇

    学习内容:
    1.学习基本概念,传输的同步阻塞式I/O编程,伪异步IO实现
    2.学习基于NIO的同步非阻塞式编程
    3.了解基于NIO2.0的异步非阻塞(AIO)编程
    ***********************************************************************************************
    ***
    1.1基本概念
    socket又称为“套接字”,应用程序通常通过“套接字”向网络发出请求或者应答网络请求。
    ServerSocket与Socket位于java.net包。ServerSocket用于服务器端,Socket是简历网络连接时使用的。在连接成功时,应用程序两端会产生一个Socket实例,完成这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,不会因为在服务端或者客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。
    套接字之间的连接过程可以分为四个步骤,服务器监听,客户端请求服务器,服务器确认,客户端确认,进行通信。
    (1)服务器监听:
     是服务器套接字并不定位具体的客户端套接字,而是等待连接的状态,实时监控网络状态。
    (2)客户端请求服务器:
     是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端套接字必须首先描述它要连接的服务器的套接字。指出相应的地址和端口。然后向服务端套接字提出连接请求。
    (3)服务器端连接确认:
     是指服务端套接字监听到或者说接收到客户端的套接字的连接请求,它就响应客户端套接字请求,建立一个新的线程,把服务端套接字的描述发给客户端。
    (4)客户端连接确认:
     一旦客户确认了此描述,连接建立好了,双方开始进行通信,而服务端套接字继续处于监听的状态,继续接收其他客户端套接字的连接请求。
    1.2 基本概念
    IO(BIO)和NIO的区别:其本质是阻塞和非阻塞的区别。
    阻塞概念:应用程序在获取网络数据的时候,如果网络传输很慢,那么程序会一直等着,知道传输完毕。
    非阻塞概念:应用程序直接可以获取已经准备就绪好的数据,无需等待。
    IO为同步阻塞形式,NIO为同步非阻塞形式。NIO并没有实现异步,在JDK1.7升级之后支持NIO2.0(AIO-异步非阻塞)。
    同步和异步:一般是面向操作系统和应用程序对IO操作层面上区别。
    同步:应用程序会直接参与IO的读写操作,并且我们的应用程序会直接阻塞到某一个方法上。直到数据准备就绪,或者采用轮询的策略实时检查数据的就绪状态。如果就绪则获取数据。
    异步:IO读写操作交给操作系统处理,与我们的应用程序没有直接的关系。我们的程序不需要关心IO读写,当操作系统完成IO读写操作时,会给我们应用程序发送通知。我们的应用程序直接拿走数据。
    同步说的是Server服务端的执行方式
    阻塞指的的具体的技术,接收数据的方式、状态(IO,NIO)。
    1.3 传统的BIO编程
    网络编程的基本模型是Client/Server模型。也就是两个进程直接进行相互通信,其中服务器提供配置信息(绑定IP和监听端口),客户端通过连接操作向服务器端监听的地址发起连接请求,通过三次握手建立连接,如果连接成功,即双方可以进行通信(网络套接字SOCKET)。

    缺点:线程较多,服务器就会奔溃。
    解决方案:没有NIO之前:伪异步
    利用线程池和任务队列,控制线程个数。
    1.4 伪异步IO
    采用线程池和任务队列实现伪异步通信框架。

    ***
    2.1 NIO模型介绍
    在介绍之前,先澄清一个概念有人叫NIO为new IO,有人把NIO叫做Non-block IO,这里我们还是习惯说后者,非阻塞IO.
    概念:
    Buffer(缓冲区),Channel(管道,通道),Selector(选择器、多路复用器)

    2.2 Buffer
    Buffer是一个对象,它包含一些要读取和写入的数据,在NIO类库中,加入Buffer对象,体现了新库和原IO的一个重要区别。在面向流的IO中,可以将数据直接写入或读取到Stream对象中。在NIO库中,所有的数据都是有缓冲区处理的(读写)。缓冲区实际是一个数组,通常它是一个字节数组(ByteBuffer),也可以用其他类型的数组。这个数组为缓冲区提供了数据访问的读写操作属性,如位置,容量,上限等概念,大家可以参考API文档。
    Buffer类型:每一种基本数据类型都有(除了Boolean):
      ● ByteBuffer
      ● CharBuffer
      ● ShortBuffer
      ● IntBuffer
      ● LongBuffer
      ● FloatBuffer
      ● DoubleBuffer
    Buffer使用:
    public static void main(String[] args) {
            //1.基本操作
            //2.初始化容量-指定长度10
            IntBuffer intBuffer=IntBuffer.allocate(10);
            intBuffer.put(13);//position:0-->13
            intBuffer.put(13);//position:1-->13
            intBuffer.put(13);//position:2-->13
            intBuffer.flip();//不复位,get()方法抛出异常,复位之后正常遍历
            intBuffer.put(1,12);
            System.out.println("容量:"+intBuffer.capacity());
            System.out.println("操作空间限制:"+intBuffer.limit());//元素个数决定
            System.out.println("获取下标为1的元素:"+intBuffer.get(2));//
            for(int i=0;i<intBuffer.limit();i++) {
                //get()用的是position
                System.out.println(intBuffer.get());
            }
            intBuffer.flip();//不复位,get()方法抛出异常,复位之后正常遍历
            for(int i=0;i<intBuffer.limit();i++) {
                //get()用的是position
                System.out.println(intBuffer.get());
            }
            //wrap方法使用
            //wrap方法会创建一个数组,这种方法不会初始化缓存的长度,因为没有意义。最还会被warp包裹的数组覆盖掉。
            //并且wrap方法修改缓冲区的时候,数组本身也会跟着变化。
            int[] arr=new int[] {1,2,5};
            IntBuffer buf=IntBuffer.wrap(arr,0,3);
            System.out.println(buf);

            //复制方法
            IntBuffer buf2=intBuffer.duplicate();
            buf2.position(1);
            //设置position:java.nio.HeapIntBuffer[pos=0 lim=3 cap=3]f
            System.out.println(buf2);
            //放到数组中
            int[] arr2=new int[buf2.remaining()];
            buf2.get(arr2);
            for(int a: arr2) {
                System.out.println(a+"+");
            }
            //2
            buf2.flip();
            System.out.println("可读区域"+buf2.remaining());
            //arr方式
            IntBuffer intBuffer2=IntBuffer.allocate(10);
            intBuffer2.put(arr);
            System.out.println(intBuffer2);
        }
    2.3 Channel
    通道(Channel),它就像来自水管一样,网络数据通过Channel读物和写入。通道与流不同的之处在于通道是双向的。而流只有一个方向移动(要么是InputStream,要么是OutputStream)。而通道可以用于读、写,或者同时进行,最重要的是可以与多路复用器结合起来,有多种状态位,方便多路复用器去识别。事实上,通道分为两类:一类是网络读写的(SelectableChannel),一类是用于文件操作的(FileChannel)。我们用的SocketChannel和ServerSocketChannel都属于SelectableChannel的子类。
    2.4 Selector
    多路复用器(Selector),他是NIO编程的基础,非常重要。多路复用器提供选择已经就绪的任务的能力。
    简单说,就是selector会不断地轮询注册在其上的通道(Channel),如果某个通道发生了读写的操作。这个通道就处于就绪状态,会被Selector轮询出来,然后SelectionKey可以取得就绪的Channel的集合,从而进行后续的IO操作。
    一个多路复用器(Selector)可以负责成千上万的Channel通道,没有上线,这也是JDK使用epoll代理传统的select实现。获得连接句柄没有限制。这也就意味着我们只要一个线程负责Selector的轮询,就可以接入成千上万的客户端,这是JDK的NIO库的巨大进步。
    Selector线程就类似一个管理者(Master),管理了成千上万个管道,然后轮询哪个管道的数据已经准备好,通知CPU执行IO的读写操作。
    Selector模式会分配给每个管道一个key值。相当于标签,select选择器就是以轮询的方式查找注册所有的IO事件(管道),当我们的IO事件(管道)准备就绪后,select就会识别,会通过key值来找到相应的管道,进行相关的数据处理操作(从管道里读和写数据,写到我们数据的缓冲区中)。
    每个管道都会选择器进行注册不同的事件状态。以便选择器查找。
    SelectionKey.OP_CONNECT
    SelectionKey.OP_ACCEPT
    SelectionKey.OP_READ
    SelectionKey.OP_WRITE
    ***
    3.1 NIO2.0(AIO)模型介绍
    AIO编程,在NIO基础之上引入了异步通道的概念,并提供了异步文件和异步套接字通道的实现。从而在真正意义上实现了异步非阻塞,之前我们学习的NIO只是非阻塞而并非异步。而AIO它不需要通过多路复用器对注册的通道进行轮询操作即可实现异步读写。从而简化了NIO编程模型。也可以称之为NIO2.0,这种模式才真正的属于我们异步非阻塞的模型。
    AsynchronousServerScoketChannel
    AsynchronousSocketChannel
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值