NIO使用方法

本文介绍了使用NIO进行服务端开发的基本步骤,包括创建ServerSocketChannel并设置为非阻塞模式,绑定监听并配置TCP参数,创建Selector并注册ServerSocketChannel监听连接事件等。通过一个简单的NioServer示例,展示了如何处理客户端连接及读取数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NIO进行服务端开发的一般步骤:

  1. 创建一个ServerSocketChannel,并配置它为非阻塞模式;
  2. 绑定监听,配置相关的TCP参数,比如backlog大小;
  3. 创建一个独立的I/O进程,用于轮询多路复用器Selector
  4. 创建Selector,将之前创建的ServerSocketChannel注册到Selector上,监听SelectionKey.ACCEPT事件
  5. 启动I/O线程,在一个循环体中执行Selecttor.selector()方法,轮询就绪的Channel
  6. 当轮询到就绪的Channel的时候,就需要对它的状态进行判断,如果是OP_ACCEPT状态,说明在这个时候,有客户端接入了,需要调用ServerSocketChannel.accept()方法接收新的客户端;
  7. 设置新接入的客户端链路SocketChannel为非阻塞模式,并配置一些TCP参数
  8. 将SocketChannel注册到Selector,监听OP_READ事件
  9. 如果轮询到了OP_READ事件,则说明在SocketChannel中有新的就绪数据包,这时候需要创建ByteBuffer读取数据包;
  10. 如果轮询到channel中的事件为OP_WRITE,说明还有数据包没有发送完成,需要继续进行发送

下面以一个简单的Demo来说明NIO的使用方法:

package study170301;

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 void run() {
        new Thread(new MultipexerTimeServer(8080)).start(); // 启动服务器
    }

    public static void main(String[] args) {
        new NioServer().run();
    }

    class MultipexerTimeServer implements Runnable {

        private Selector selector;
        private ServerSocketChannel serverChannel;
        private volatile boolean stop;

        public MultipexerTimeServer(int port) {
            try {
                serverChannel = ServerSocketChannel.open();
                selector = Selector.open();
                serverChannel.configureBlocking(false);
                serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册连接事件
                serverChannel.socket().bind(new InetSocketAddress(port), 1024);
                System.out.printf("Server is started in %s:\n", port);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void stop() {
            this.stop = true;
        }

        private void handleEvent(SelectionKey key) {
            // 只有在key合法的情况下,才对相应的事件做处理
            if (key.isValid()) {
                // 处理客户端的连接事件
                if (key.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    try {
                        SocketChannel sc = ssc.accept();
                        sc.configureBlocking(false);
                        sc.register(selector, SelectionKey.OP_READ); // 注册可读事件
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }

                // 处理读事件
                if (key.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024); // 创建一个1024的空间
                    try {
                        int bytes = socketChannel.read(readBuffer);
                        if (bytes > 0) {
                            readBuffer.flip(); // 从缓存中出数据
                            byte[] bt = new byte[readBuffer.remaining()]; // 创建一个byte,用来装数据
                            readBuffer.get(bt);
                            System.out.println(new String(bt)); // 输出从客户端读到的数据
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                System.out.println("key is not valid...");
            }
        }

        @Override
        public void run() {
            while (!stop) {
                try {
                    selector.select(1000);
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> it = selectedKeys.iterator();
                    SelectionKey key = null;
                    while (it.hasNext()) {
                        key = it.next();
                        it.remove();
                        // 在这里对key进行处理
                        handleEvent(key);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } // 设置轮询扫描的时间间隔
            }
        }

    }

}
Java NIO中的Channel是用于在Java程序中进行I/O操作的基本组件之一。Channel提供了一种高效的、可扩展的方式来进行数据传输,同时也支持非阻塞式I/O操作。 下面是一些常用的Channel方法: 1. `open()`:打开一个新的Channel对象。 2. `close()`:关闭当前的Channel对象。 3. `read(ByteBuffer dst)`:从Channel中读取数据到指定的ByteBuffer中。 4. `write(ByteBuffer src)`:将数据从指定的ByteBuffer写入到Channel中。 5. `configureBlocking(boolean block)`:设置当前Channel的阻塞模式,如果为true则为阻塞模式,如果为false则为非阻塞模式。 6. `register(Selector sel, int ops)`:将当前的Channel对象注册到指定的Selector中,并且指定关注的事件类型。 7. `isOpen()`:判断当前的Channel对象是否处于打开状态。 8. `isConnected()`:判断当前的Channel对象是否已经连接到远程服务器。 9. `finishConnect()`:完成Channel对象的连接操作,如果连接成功则返回true,否则返回false。 10. `bind(SocketAddress local)`:将当前的Channel对象绑定到指定的本地地址。 11. `getRemoteAddress()`:获取当前Channel对象连接的远程服务器地址。 12. `getLocalAddress()`:获取当前Channel对象绑定的本地服务器地址。 这些方法提供了基本的Channel操作,可以根据具体的需求进行使用。需要注意的是,Channel对象在使用完毕后需要调用close()方法进行关闭,否则可能会出现资源泄漏等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值