栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

网络I/o编程模型6 Nio之Selector以及NIO客户服务通讯

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

网络I/o编程模型6 Nio之Selector以及NIO客户服务通讯

一 selector 1.1 介绍
  1. java的NIO,用非阻塞的IO方式,可以用一个线程,处理多个的客户端连接,就会使用到selector(选择器)。

  2. netty的IO线程NIOEventLoop聚合了selector(选择器,也叫多路复用器),可以同时并发处理成百上千各个客户端连接。

  3. 当线程从某客户端socket通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。

  4. 只有在连接/通道真正有读写事件发生时,才会进行读写,就大大地减少了系统的开销,并且不必为每个连接都创建一个线程,不用去维护多个线程。避免了多线程之间的上下文切换导致系统的开销。

  5. 一个I/O线程可以并发处理N个哭护短连接和读写操作,这从根本上解决了传统同步阻塞I/O一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。

1.2 常用方法

selector.select();//阻塞

selector.select(1000);//阻塞1000毫秒,在1000毫秒后返回

selector.wakeup();//唤醒 selector

selector.selectNow();//不阻塞,立即返回

1.3 selectionKey

selectionKey:表示selector和网络通道的注册关系。
int  OP_ACCEPT 有新的网络连接可以accept ,值为16
int  OP_CONNECT 代表连接已经建立,值为8
int  OP_READ   代表读操作,值为1
int  OP_WRITE  代表写操作,值为4

1.4 ServerSocketChannel

serversocketchannel:作用是在服务器端监听新的客户端socket连接。

1.5 socketchannel

网络IO通道,具体负责进行读写操作。NIO把缓冲区的数据写入通道,或者把通道里的数据读到缓存区。

 

1.6 selector和socketchannel和selectorkey的关系

1.当客户端连接时,会通过ServerSocketChannel得到socketchannel
2.selector进行监听select方法,返回有事件发生的通道的个数;
3.将socketchannel注册到selector上,一个selector上可以注册多个socketchannel
4.注册后返回一个selectorkey,会和该selector关联。

二 代码实操 2.1 使用Nio的非网络阻塞机制,实现客户端和网络端进行通讯。

1 代码

package com.ljf.netty.nio.cs;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;


public class NioServer {
    public static void main(String[] args) throws IOException {
        //创建ServerSocketCHannel->  ServerSocket
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        //得到一个selector对象
        Selector selector=Selector.open();
        //绑定一个端口6666,在服务器端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        //设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //把serverSocketChannel注册到selector关心的事件为OP_Accept
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //循环等待客户端的连接
        while(true){
            //这里我们等待设置1秒,如果没有事件发生,则返回
            //selector.select()>0.则就获取到相关的selectionKey集合,也表示已经获取到关注的事件集合
            //System.out.println("selector.select():"+selector.select());
            if(selector.select(1000)==0){
                System.out.println("服务器等待1秒,无连接");
                continue;
            }
            //2.selector.selectedKeys() 返回关注事件的集合
            Set selectionKeys=selector.selectedKeys();
            //3.进行遍历,获取可用
            Iterator keyIterator=selectionKeys.iterator();
            while(keyIterator.hasNext()){
                //获取到selectionKey
                SelectionKey key=keyIterator.next();
                //根据key对应的通道发生相应的事件,做出相应事件处理
                if (key.isAcceptable()){//如果是OP_ACCEPT,则又新客户端连接
                    //该客户端生成一个socketchannel
                    SocketChannel socketChannel=serverSocketChannel.accept();
                    System.out.println("客户端连接成功,生成了一个 socketChannel"+socketChannel.hashCode());
                    //将 socketChannel设置为非阻塞
                    socketChannel.configureBlocking(false);
                    //将socketchannel注册到selector,关于事件为OP_READ,同时给socketchannel
                    //关联一个buffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if(key.isReadable()){//开始op_read
                    //通过key,反向获取到对应的channel
                    SocketChannel channel=(SocketChannel) key.channel();
                    //获取到该channel关联的buffer
                    ByteBuffer buffer=(ByteBuffer) key.attachment();
                    channel.read(buffer);
                    System.out.println("from 客户端:"+new String(buffer.array()));
                }
                //手动从集合移动当前selectionKey,防止重复操作
                keyIterator.remove();
                System.out.println("========================");
            }

        }

    }
}

客户端:

package com.ljf.netty.nio.cs;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;


public class NioClient {
    public static void main(String[] args) throws IOException {
        //得到一个网络通道
        SocketChannel socketChannel=SocketChannel.open();
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //提供服务器端的ip和端口
        InetSocketAddress inetSocketAddress=new InetSocketAddress("127.0.0.1",6666);
        //连接服务器
        if(!socketChannel.connect(inetSocketAddress)){
            while(!socketChannel.finishConnect()){
                System.out.println("连接客户端需要一些时间,因为是非阻塞io,暂时可以先做其他工作!");
            }
        }
        //连接成功
        String string="hello,北京";
        ByteBuffer buffer=ByteBuffer.wrap(string.getBytes());
        //返送数据,将buffer中的数据写入到channel
        socketChannel.write(buffer);
        System.in.read();
    }
}

3.结果

启动一个服务端,两个客户端

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/887301.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号