java nio基本解读

 

目录

1. Buffer

1.1 ByteBufferAsCharBufferB 和 ByteBufferAsCharBufferL 

1.2 DirectCharBufferU 和 DirectCharBufferS

1.3 HeapCharBuffer 和 HeapCharBufferR

2. Channel

2.1 NetworkChannel 接口

2.1.1 SocketChannel 类

2.1.2 ServerSocketChannel 类

3. Selector 多路选择器

3.1 AbstractSelector 抽象类

4. InetSocketAddress 类

5. SelectionKey 类

6. 简单的例子

6.1 客户端

6.2 服务端


NIO的三个核心:Buffer、Channel 和 Selector。

               

channel 与套接字相连接,接收套接字的数据和写数据进套接字都要经过 channel,而 channel 又注册在 selector 中,因此,selector 有能力获取到所有 channel 中准备好读写的那些 channel,以便我们进行读写;不管是读还是写,数据都需要被存储在某个地方,这个地方就是 buffer。可以看出,一个线程就完成了上述的事情,因此IO多路复用的效率很高。

1. Buffer

下图是缓冲区系列的框架图,一共封装了7种基本数据类型的缓冲区(除了 boolean 类型的),但是都是抽象类,不能直接使用。

Buffer 类是所有缓冲区的根类,其中定义了缓冲区的基本属性和框架。提一下 address 这个变量,表示一个地址,可以是内核态的缓冲区地址,也可以是套接字缓冲区地址(其实也是内核态)。 

public abstract class Buffer {
    static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
    // 标记索引
    private int mark = -1;
    // 下一个将要读取的位置
    private int position = 0;
    // 限制索引
    private int limit;
    // 缓冲区空间大小
    private int capacity;

    long address;
    // 构造方法
    Buffer(int mark, int pos, int lim, int cap) {  
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        this.capacity = cap;
        limit(lim);
        position(pos);
        if (mark >= 0) {
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: ("
                                                   + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }
    public final int capacity() {     return capacity;   }
    public final int position() {     return position;   }
    public final int limit() {      return limit;    }
    final int markValue() {         return mark;    }
    // 设置新position
    public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }
    // 设置新limit
    public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }
    // 将当前的 position 做标记
    public final Buffer mark() {
        mark = position;
        return this;
    }
    // 让 position 重回 mark 处
    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }
    // 清空 buffer,但是空间还在
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }
    // 读写模式的切换,即从读模式转为写模式,或者从写模式转为读模式
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
    // 从头开始,标记失效
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
    // 还剩多少可读或者还剩多少空间可写入
    public final int remaining() {        return limit - position;    }
    // 是否还有剩余
    public final boolean hasRemaining() {        return position < limit;    }
    public abstract boolean isReadOnly();
    public abstract boolean hasArray();
    public abstract Object array();
    public abstract int arrayOffset();
    public abstract boolean isDirect();
    // 往后跳一个位置
    final int nextGetIndex() {                          
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }
    // 和上面一个样,不过意义不同而已
    final int nextPutIndex() {                          
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }
    // 往后跳nb个位置
    final int nextGetIndex(int nb) {                    
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }
    final int nextPutIndex(int nb) {
        if (limit - position < nb)
            throw new BufferOverflowException();
        int p = position;
        position += nb;
        return p;
    }
    // 将缓冲区设置无效
    final void truncate() { 
        mark = -1;
        position = 0;
        limit = 0;
        capacity = 0;
    }
    // 是标记失效
    final void discardMark() {        mark = -1;    }
}

对于每一类 buffer,都有三大类子系列。拿 CharBuffer 类举例说明:

1.1 ByteBufferAsCharBufferB 和 ByteBufferAsCharBufferL 

ByteBufferAsCharBufferB 类是将 ByteBuffer 进行一层封装,提供的方法都是字符层面的,使用大端序列。

ByteBufferAsCharBufferL 类是将 ByteBuffer 进行一层封装,提供的方法都是字符层面的,使用小端序列。

ByteBufferAsCharBufferRB 继承 ByteBufferAsCharBufferB 类,是 ByteBufferAsCharBufferB 类的可读版本。

ByteBufferAsCharBufferRL 继承 ByteBufferAsCharBufferL 类,是 ByteBufferAsCharBufferL 类的可读版本。

1.2 DirectCharBufferU 和 DirectCharBufferS

DirectBuffer 系列是内核态的缓冲区,而不是用户态的缓冲区,JVM所管理的内存都是属于用户态,如果需要进行IO的话,比如将数据存入磁盘,数据需要经历用户态、内核态、磁盘,如果我们使用 DirectBuffer 系列的话,就直接将数据写入内核态,于是数据只需要经历内核态和磁盘,这种技术就叫零拷贝技术。

DirectCharBufferU 类是内核态字符缓冲区,U表示与平台字节序列保持一致。

DirectCharBufferS 类是内核态字符缓冲区,U表示与平台字节序列不一致。

DirectCharBufferRU 类继承 DirectCharBufferU,是 DirectCharBufferU 类的可读版本。但是目前JDK8中没有实现,因此需要自己去实现。

DirectCharBufferRS 类继承 DirectCharBufferS,是 DirectCharBufferS 类的可读版本。但是目前JDK8中没有实现,因此需要自己去实现。

1.3 HeapCharBuffer 和 HeapCharBufferR

HeapBuffer 系列是我们平常用的缓冲区,是JVM堆中的缓冲区。HeapCharBufferR 继承 HeapCharBuffer 类,是它的可读版本。

2. Channel

下图是 Channel 系列的框架图,根据功能,通道被分为5大类。

Channel 接口是定义通道基本规范的,就2个方法,如下。 Channel 通道就相当于鼻子一样,nio 下面的输入输出都需要靠通道来进行,读取数据(吸气),需要通过嘴巴,写出数据(呼气),也是需要通过嘴巴。

public interface Channel extends Closeable {
    // 查看通道是否开启
    public boolean isOpen();
    // 关闭通道
    public void close() throws IOException;
}

2.1 NetworkChannel 接口

这个是用于网络系列的,专用于 Socket。

然后看看 NetworkChannel 接口的定义。

public interface NetworkChannel extends Channel
{
    // 将此通道与套接字进行绑定
    NetworkChannel bind(SocketAddress local) throws IOException;
    // 获得套接字的信息
    SocketAddress getLocalAddress() throws IOException;
    // 设置套接字的属性
    <T> NetworkChannel setOption(SocketOption<T> name, T value) throws IOException;
    // 获得套接字的属性
    <T> T getOption(SocketOption<T> name) throws IOException;
    // 获得此通道绑定的所有套接字的属性集
    Set<SocketOption<?>> supportedOptions();
}

2.1.1 SocketChannel 类

选择器是实现IO多路复用的关键,selector 具体是如何实现的,不同的操作系统还不一样,一共有四种方式:select、poll、epoll、kqueue,因此,SelectorProvider 抽象类的作用(当然具体的实现还是要靠它的子实现类)就是创建平台默认使用的 selector,创建 socketChannel。

public abstract class SocketChannel
    extends AbstractSelectableChannel
    implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
{
    // 构造方法,参数是一个选择器提供者
    protected SocketChannel(SelectorProvider provider) {
        super(provider);
    }
    // 创建一个 socketChannel
    public static SocketChannel open() throws IOException {
        return SelectorProvider.provider().openSocketChannel();
    }
    // 创建一个 socketChannel,并将它与指定的远程套接字连接
    public static SocketChannel open(SocketAddress remote)
        throws IOException
    {
        SocketChannel sc = open();
        try {
            sc.connect(remote);
        } catch (Throwable x) {
            try {
                sc.close();
            } catch (Throwable suppressed) {
                x.addSuppressed(suppressed);
            }
            throw x;
        }
        assert sc.isConnected();
        return sc;
    }
    // 返回此 channel 支持的操作集,每个操作是一个数字,因此多个数字 | 操作得到的值就可以看出有
    // 哪些操作
    public final int validOps() {
        return (SelectionKey.OP_READ
                | SelectionKey.OP_WRITE
                | SelectionKey.OP_CONNECT);
    }
    // 将通道与本地主机上的套接字绑定
    @Override
    public abstract SocketChannel bind(SocketAddress local)
        throws IOException;
    // 设置通道的属性
    @Override
    public abstract <T> SocketChannel setOption(SocketOption<T> name, T value)
        throws IOException;
    // 关闭通道的输入功能,无法从通道读取数据了
    public abstract SocketChannel shutdownInput() throws IOException;
    // 关闭通道的输出功能,无法往通道里写入数据了
    public abstract SocketChannel shutdownOutput() throws IOException;
    // 返回与此通道连接的套接字
    public abstract Socket socket();
    // 判断此通道是否还与套接字处于连接状态
    public abstract boolean isConnected();
    // 判断此通道是否正在连接一个套接字
    public abstract boolean isConnectionPending();
    // 连接一个远程的套接字
    public abstract boolean connect(SocketAddress remote) throws IOException;
    // 断开与套接字的连接
    public abstract boolean finishConnect() throws IOException;
    // 返回远程套接字
    public abstract SocketAddress getRemoteAddress() throws IOException;
    public abstract int read(ByteBuffer dst) throws IOException;
    public abstract long read(ByteBuffer[] dsts, int offset, int length)
        throws IOException;
    public final long read(ByteBuffer[] dsts) throws IOException {
        return read(dsts, 0, dsts.length);
    }
    public abstract int write(ByteBuffer src) throws IOException;
    // 向通道写入srcs的某段数据
    public abstract long write(ByteBuffer[] srcs, int offset, int length)
        throws IOException;
    // 向通道写入数据
    public final long write(ByteBuffer[] srcs) throws IOException {
        return write(srcs, 0, srcs.length);
    }
    // 返回此 channel 绑定的套接字
    @Override
    public abstract SocketAddress getLocalAddress() throws IOException;

}

2.1.2 ServerSocketChannel 类

这是监听套接字的通道,有几个方法是和上面一样的,剩下的方法如下。

public abstract class ServerSocketChannel
    extends AbstractSelectableChannel
    implements NetworkChannel
{
    protected ServerSocketChannel(SelectorProvider provider) {
        super(provider);
    }
    public static ServerSocketChannel open() throws IOException {
        return SelectorProvider.provider().openServerSocketChannel();
    }
    public final int validOps() {
        return SelectionKey.OP_ACCEPT;
    }
    public final ServerSocketChannel bind(SocketAddress local)
        throws IOException
    {
        return bind(local, 0);
    }
    // 绑定本机的套接字,backlog 表示接受的最大连接
    public abstract ServerSocketChannel bind(SocketAddress local, int backlog)
        throws IOException;
    // 设置属性
    public abstract <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
        throws IOException;
    // 返回与此channel连接的socket
    public abstract ServerSocket socket();
    // 接收一个连接,并创建一个新的channel与该连接绑定,然后返回该channel
    public abstract SocketChannel accept() throws IOException;
    // 返回套接字的信息
    @Override
    public abstract SocketAddress getLocalAddress() throws IOException;

}

3. Selector 多路选择器

选择器的实现就比较单一,如下图。

先看一下Selector抽象类中定义的基本规范。

public abstract class Selector implements Closeable {
    protected Selector() { }
    // 创建一个selector
    public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }
    // 判断selector是否开启
    public abstract boolean isOpen();
    // 返回创建这个selector 的 selectorProvider
    public abstract SelectorProvider provider();
    // 返回selector管理的所有通道的标识信息
    public abstract Set<SelectionKey> keys();
    // 返回当前可读写的通道的标识信息
    public abstract Set<SelectionKey> selectedKeys();
    // 返回当前可读写的通道数量
    public abstract int selectNow() throws IOException;
    // 返回当前时刻经过timeout时间过程中,可读写的通道数量
    public abstract int select(long timeout)
        throws IOException;
    // 返回当前可读写的通道数量,如果没有可读写的通道,则阻塞
    public abstract int select() throws IOException;
    // 唤醒处于阻塞状态中的select操作
    public abstract Selector wakeup();
    // 关闭这个selector
    public abstract void close() throws IOException;

}

3.1 AbstractSelector 抽象类

接下来看看这个AbstractSelector 都实现了什么,扩展了哪些,下面给了一个简单的说明。

public abstract class AbstractSelector
    extends Selector
{
    // 一个代表此selector是否处于开启的线程安全boolean变量
    private AtomicBoolean selectorOpen = new AtomicBoolean(true);
    // 创建这个selector 的 selectorProvider
    private final SelectorProvider provider;
    protected AbstractSelector(SelectorProvider provider) {
        this.provider = provider;
    }
    // 用于存储要取消的通道
    private final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>();
    void cancel(SelectionKey k) {                      
        synchronized (cancelledKeys) {
            cancelledKeys.add(k);
        }
    }
    // 关闭此selector
    public final void close() throws IOException {
        boolean open = selectorOpen.getAndSet(false);
        if (!open)
            return;
        implCloseSelector();
    }
    protected abstract void implCloseSelector() throws IOException;
    // 判断selector是否开启
    public final boolean isOpen() {
        return selectorOpen.get();
    }
    public final SelectorProvider provider() {
        return provider;
    }
    protected final Set<SelectionKey> cancelledKeys() {
        return cancelledKeys;
    }
    // 将通道注册进selector
    protected abstract SelectionKey register(AbstractSelectableChannel ch,
                                             int ops, Object att);
    // 将通道从selector中注销
    protected final void deregister(AbstractSelectionKey key) {
        ((AbstractSelectableChannel)key.channel()).removeKey(key);
    }
    private Interruptible interruptor = null;

    protected final void begin() {
        if (interruptor == null) {
            interruptor = new Interruptible() {
                    public void interrupt(Thread ignore) {
                        AbstractSelector.this.wakeup();
                    }};
        }
        AbstractInterruptibleChannel.blockedOn(interruptor);
        Thread me = Thread.currentThread();
        if (me.isInterrupted())
            interruptor.interrupt(me);
    }

    protected final void end() {
        AbstractInterruptibleChannel.blockedOn(null);
    }

}

4. InetSocketAddress 类

SocketAddress 是一个抽象类,内容为空,InetSocketAddress 是 SocketAddress 类的实现类,用于封装一个套接字的相关信息。

总共三个要点:主机名,地址,端口号。其中当指定主机名时,会对这个主机名进行DNS解析,判断这个主机名是否能够被解析。

public class InetSocketAddress
    extends SocketAddress
{
    
    private static class InetSocketAddressHolder {
        // 套接字的名字
        private String hostname;
        // 套接字IP
        private InetAddress addr;
        // 套接字端口号
        private int port;
        private InetSocketAddressHolder(String hostname, InetAddress addr, int port) {
            this.hostname = hostname;
            this.addr = addr;
            this.port = port;
        }

        private int getPort() {            return port;        }
        private InetAddress getAddress() {            return addr;        }
        private String getHostName() {
            if (hostname != null)
                return hostname;
            if (addr != null)
                return addr.getHostName();
            return null;
        }
        private String getHostString() {
            if (hostname != null)
                return hostname;
            if (addr != null) {
                if (addr.holder().getHostName() != null)
                    return addr.holder().getHostName();
                else
                    return addr.getHostAddress();
            }
            return null;
        }
        private boolean isUnresolved() {            return addr == null;        }
        @Override
        public String toString() {
            if (isUnresolved()) {
                return hostname + ":" + port;
            } else {
                return addr.toString() + ":" + port;
            }
        }
        @Override
        public final boolean equals(Object obj) {
            if (obj == null || !(obj instanceof InetSocketAddressHolder))
                return false;
            InetSocketAddressHolder that = (InetSocketAddressHolder)obj;
            boolean sameIP;
            if (addr != null)
                sameIP = addr.equals(that.addr);
            else if (hostname != null)
                sameIP = (that.addr == null) &&
                    hostname.equalsIgnoreCase(that.hostname);
            else
                sameIP = (that.addr == null) && (that.hostname == null);
            return sameIP && (port == that.port);
        }
        @Override
        public final int hashCode() {
            if (addr != null)
                return addr.hashCode() + port;
            if (hostname != null)
                return hostname.toLowerCase().hashCode() + port;
            return port;
        }
    }

    private final transient InetSocketAddressHolder holder;

    private static final long serialVersionUID = 5076001401234631237L;

    private static int checkPort(int port) {
        if (port < 0 || port > 0xFFFF)
            throw new IllegalArgumentException("port out of range:" + port);
        return port;
    }

    private static String checkHost(String hostname) {
        if (hostname == null)
            throw new IllegalArgumentException("hostname can't be null");
        return hostname;
    }

    // 创建一个套接字地址信息,指定端口号,至于IP的话,就看主机上有几个网卡了,任意选择一个IP
    public InetSocketAddress(int port) {
        this(InetAddress.anyLocalAddress(), port);
    }
    // 创建一个套接字地址信息,指定IP和端口号
    public InetSocketAddress(InetAddress addr, int port) {
        holder = new InetSocketAddressHolder(
                        null,
                        addr == null ? InetAddress.anyLocalAddress() : addr,
                        checkPort(port));
    }
    // 给定主机名和端口号,创建一个套接字地址
    public InetSocketAddress(String hostname, int port) {
        checkHost(hostname);
        InetAddress addr = null;
        String host = null;
        try {
            addr = InetAddress.getByName(hostname);
        } catch(UnknownHostException e) {
            host = hostname;
        }
        holder = new InetSocketAddressHolder(host, addr, checkPort(port));
    }

    private InetSocketAddress(int port, String hostname) {
        holder = new InetSocketAddressHolder(hostname, null, port);
    }

    // 给定主机名和端口号,创建一个未被解析的套接字地址
    public static InetSocketAddress createUnresolved(String host, int port) {
        return new InetSocketAddress(checkPort(port), checkHost(host));
    }
    /**
     * @serialField hostname String
     * @serialField addr InetAddress
     * @serialField port int
     */
    private static final ObjectStreamField[] serialPersistentFields = {
         new ObjectStreamField("hostname", String.class),
         new ObjectStreamField("addr", InetAddress.class),
         new ObjectStreamField("port", int.class)};

    private void writeObject(ObjectOutputStream out)
        throws IOException
    {
        // Don't call defaultWriteObject()
         ObjectOutputStream.PutField pfields = out.putFields();
         pfields.put("hostname", holder.hostname);
         pfields.put("addr", holder.addr);
         pfields.put("port", holder.port);
         out.writeFields();
     }

    private void readObject(ObjectInputStream in)
        throws IOException, ClassNotFoundException
    {
        // Don't call defaultReadObject()
        ObjectInputStream.GetField oisFields = in.readFields();
        final String oisHostname = (String)oisFields.get("hostname", null);
        final InetAddress oisAddr = (InetAddress)oisFields.get("addr", null);
        final int oisPort = oisFields.get("port", -1);

        // Check that our invariants are satisfied
        checkPort(oisPort);
        if (oisHostname == null && oisAddr == null)
            throw new InvalidObjectException("hostname and addr " +
                                             "can't both be null");

        InetSocketAddressHolder h = new InetSocketAddressHolder(oisHostname,
                                                                oisAddr,
                                                                oisPort);
        UNSAFE.putObject(this, FIELDS_OFFSET, h);
    }

    private void readObjectNoData()
        throws ObjectStreamException
    {
        throw new InvalidObjectException("Stream data required");
    }

    private static final long FIELDS_OFFSET;
    private static final sun.misc.Unsafe UNSAFE;
    static {
        try {
            sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
            FIELDS_OFFSET = unsafe.objectFieldOffset(
                    InetSocketAddress.class.getDeclaredField("holder"));
            UNSAFE = unsafe;
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
    }
    // 返回端口
    public final int getPort() {
        return holder.getPort();
    }
    // 返回地址
    public final InetAddress getAddress() {
        return holder.getAddress();
    }
    // 返回主机名
    public final String getHostName() {
        return holder.getHostName();
    }
    // 返回主机名或者地址的字符串形式
    public final String getHostString() {
        return holder.getHostString();
    }
    // 检查地址是否已被解析。
    public final boolean isUnresolved() {
        return holder.isUnresolved();
    }
    @Override
    public String toString() {
        return holder.toString();
    }
    @Override
    public final boolean equals(Object obj) {
        if (obj == null || !(obj instanceof InetSocketAddress))
            return false;
        return holder.equals(((InetSocketAddress) obj).holder);
    }
    @Override
    public final int hashCode() {
        return holder.hashCode();
    }
}

5. SelectionKey 类

selectionKey 类是个抽象类,类似于通道的一个描述符,通过这个描述符,我们可以了解通道的实时状态。

public abstract class SelectionKey {
    protected SelectionKey() { }
    // 返回此key对应的通道
    public abstract SelectableChannel channel();
    // 返回此key对应的selector
    public abstract Selector selector();
    // 判断此key是否有效,如果此key返回了,或者它的通道返回了,selector返回了,那key就无效
    public abstract boolean isValid();
    // 请求取消此键的通道及其选择器的注册。在返回时,该键将无效,并将被添加到其选择器的取消键集。    
    // 在下一次选择操作中,该键将从选择器的所有键集中删除。
    public abstract void cancel();
    // 返回此key的兴趣集合
    public abstract int interestOps();
    // 设置兴趣集为指定值
    public abstract SelectionKey interestOps(int ops);
    // 
    public abstract int readyOps();

    public static final int OP_READ = 1 << 0;
    public static final int OP_WRITE = 1 << 2;
    public static final int OP_CONNECT = 1 << 3;
    public static final int OP_ACCEPT = 1 << 4;
    // 测试此key对应的通道是否可读
    public final boolean isReadable() {
        return (readyOps() & OP_READ) != 0;
    }
    // 测试此key对应的通道是否可写
    public final boolean isWritable() {
        return (readyOps() & OP_WRITE) != 0;
    }
    // 测试此key对应的通道是否已经完成套接字的连接
    public final boolean isConnectable() {
        return (readyOps() & OP_CONNECT) != 0;
    }
    // 判断此key对应的通道是否有accept事件
    public final boolean isAcceptable() {
        return (readyOps() & OP_ACCEPT) != 0;
    }
    private volatile Object attachment = null;
    private static final AtomicReferenceFieldUpdater<SelectionKey,Object>
        attachmentUpdater = AtomicReferenceFieldUpdater.newUpdater(
            SelectionKey.class, Object.class, "attachment"
        );
    public final Object attach(Object ob) {
        return attachmentUpdater.getAndSet(this, ob);
    }
    public final Object attachment() {
        return attachment;
    }

}

6. 简单的例子

6.1 客户端

public class NioClient {
    public static void main(String[] args){
        try {
            String hello = "hello,how are you";
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress("192.168.1.104", 4904));
            socketChannel.configureBlocking(false);
            for(int i = 0; i < 10; i++) {
                socketChannel.write(ByteBuffer.wrap(hello.getBytes()));
            }
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

6.2 服务端

public class NioServer {
    private Selector selector = null;
    private ServerSocketChannel serverSocketChannel = null;
    private SocketAddress socketAddress = null;
    private ByteBuffer heapByteBuffer = ByteBuffer.allocate(1024);
    public NioServer(){    }
    public static void main(String[] args){
        NioServer server = new NioServer();
        server.socketAddress = new InetSocketAddress(4904);
        try {
            server.selector = Selector.open();
            server.serverSocketChannel = ServerSocketChannel.open();
            server.serverSocketChannel.bind(server.socketAddress, 2);
            server.serverSocketChannel.configureBlocking(false);
            server.serverSocketChannel.register(server.selector, SelectionKey.OP_ACCEPT);
            while(true){
                if(server.selector.select() > 0){
                    Set<SelectionKey> readyIOs = server.selector.selectedKeys();
                    Iterator<SelectionKey> iterator = readyIOs.iterator();
                    while(iterator.hasNext()){
                        SelectionKey selectionKey = iterator.next();
                        iterator.remove();
                        if(selectionKey.isAcceptable()){
                            handlerAccept(server.serverSocketChannel, server.selector);
                        }else if(selectionKey.isReadable()){
                            handlerRead(selectionKey, server.heapByteBuffer);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void handlerAccept(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

    public static void handlerRead(SelectionKey selectionKey, ByteBuffer byteBuffer) throws IOException {
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        int count = socketChannel.read(byteBuffer);
        if(count > 0){
            byte[] bytes = byteBuffer.array();
            String str = new String(bytes, 0, count);
            System.out.println("服务端收到:" + str);
        }
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值