写点什么

8.6 非阻塞网络 I/O

用户头像
张荣召
关注
发布于: 2020 年 11 月 16 日

1.计算机之间如何进行网络请求?

Socket,端口:



2.服务端--客户端



########################################################################

2.多线程服务端--客户端



########################################################################

3.线程池服务器



4.BIO:Blocking I/O 阻塞I/O

   阻塞I/O: I/O操作时,用户线程i一直阻塞,直到写操作后者读操作完成。

 



5.Socket接收数据,系统内核的处理过程 

    解析:阻塞点:1.CPU拷贝数据期间,线程不被唤醒,处于阻塞状态。

                           2.接收缓冲区数据处理完毕,没有数据,线程再次被阻塞。

    特点:线程A大部分时间处于被阻塞状态<==网络数据处理比较慢(几百毫秒----几秒钟),资源利用率比较低。

    问题:如何提高性能?

    分析:不要使线程阻塞,不要使用很多的线程处理网络通信。是否可以使用一个线程以非阻塞方式处理所有并发请求?

线程读写操作不阻塞。

6.非阻塞IO:Non-Blocking I/O

   非阻塞I/O:I/O操作立即返回,发起线程不会阻塞等待。

   非阻塞read操作:

  • Socket接收缓冲区有数据,读n个(不保证数据被读完整,因此可能需要多次读)

  • Socket缓冲区没数据,则返回失败(不会等待)

  非阻塞write:

  • Socket发送缓冲区满,返回失败(不会等待)

  • Socket发送缓冲区不满,写n个数据(不保证一次性全部写入,因此可能需要多次写。)      

7.阻塞IO VS 非阻塞IO

  ServerSocket serverSocket=new ServerSocket(port);

  while(true){

         final Socket socket=serverSocket.accept();//等待请求进来。没有请求进来,当前线程则阻塞在这里。

         new Thread( new Runnable{

              @Override

              public void run(){

                    InputStream in=socket.getInputStream();

                    OutputStream out=socket.getOutputStream();

                    byte[] content=new byte[1024];

                    int len;

                    StringBuilder sb=new StringBuilder();

                    while(len=in.read(content)!=-1){

                           //读取数据。如果没有数据,当前线程则阻塞在这里,等待数据。

                           //当数据到达,网卡通知CPU,CPU拷贝数据到接收缓冲区。

                           //拷贝完成,唤醒线程。读取线程,处理数据。

                           //线程被阻塞后,不能复用。

                          sb.append(new String(content,0,len));

                    }

                    out.write("Receive Success!\n"+sb.getBytes());

                    out.flush();

                    socket.shutdownOutput();

              }

         }).start();

  }



InetSocketAddress address=new InetSocketAddress(8888);

ServerSocketChannel server=ServerSocketChannel.open();//启动server

server.bind(address);

server.configureBlocking(false);//配置非阻塞

Selector=Selector.open();

server.register(selector,SelectionKey.OP_ACCEPT);

while(true){

      selector.select();//检查selector注册事件,哪些事件到达selector。

      Iterator<SelectionKey> keys=selector.selectedKeys().iterator();//事件到达,获取selectionKey.

      while(keys.hasNext(){

            SelectionKey key=keys.next();

             ByteBuffer buffer=ByteBuffer.allocate(1024);

            if(key.isAcceptable()){//Accept事件发生。

                  ServerSocketChannel serverChannel=(ServerSocketChannel)key.channel();//找到Server

                  SocketChannel channel=serverChannel.accept();//建立新的通道。

                  channel.configureBlocking(false);//新的通道上配置非阻塞方式。

                  channel.register(selector,SelectionKey.OP_READ);//新的通道上注册新的事件:Read事件。监听Read事件

            }else if(key.isReadable()){

                  SocketChannel channel=(SocketChannel)key.channel();//Read事件通道

                  channel.read(buffer);

                  ........................................

                  channel.register(selector,SelectionKey.OP_WRITE);//通道注册Write事件

            }else if(key.isWritable()){

                  SocketChannel channel=(SocketChannel)key.channel();//通道

                  channel.write(buffer);

                  ........................................

                  channel.register(selector,SelectionKey.OP_READ);//写入完毕,注册READ事件

            }

            keys.remove();

      }

}



8.系统IO复用方式:select,poll,epoll





9.select(poll)管理下的read过程



        解析:selector遍历所有sokect,判断socket上发生的事件。(selector不知道事件发生在那个socket上)

        缺点:高并发场景:1W+并发,selector遍历所有sokect,消耗很多资源。



10.epoll管理下的read过程



解析: eventpoll(操作系统支持):socket事件发生时,构建一个event事件列表,指明哪些sokcet上有事件到达。

           遍历事件列表,就可以找到对应的socket。

           selector不用遍历所有的sokcet,只需要遍历eventpoll,就可以找到对应的socket。

           ====>效率比较高。

多路复用网络通信: 多个channel复用一个selector.

11.无活动连接时,Selector.select方法被阻塞



用户头像

张荣召

关注

还未添加个人签名 2018.05.02 加入

还未添加个人简介

评论

发布
暂无评论
8.6非阻塞网络I/O