我的游戏之路——服务器之NIO篇

来源:互联网 发布:嵌入式linux桌面系统 编辑:程序博客网 时间:2024/06/08 17:11

        前面已经讲了传统的同步阻塞IO,缺点也是很明显的。所以jdk1.4后非阻塞的NIO遍诞生了。nio主要的几个特点如下:

       1、创建一个线程负责处理IO和IO事件的分发

       2、事件驱动机制并非同步的监视事件,而是事件到达后触发

      3、线程间通过wait,notify等方式通信减少了不必要的线程切换(提高可见性,避免了上下文切换,和调度。如果有时间以后,或许会单独开一个多线程系列?)


     NIO是同步非阻塞IO,实现模式为:客户端发送的请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求才启动一个线程用于处理。当然这里也得力于双向通道的channel(相当于全双工通信,可同时进行输入输出)。此外buffer也提高了对应的缓存,关于nio的教程网上有很多这里就不一一详描述了。

     nio的客户端和服务器都需要维护一个通道管理对象Selector,用于检测通道上的事件,服务端轮询访问selector,如果Selector上注册有对应事件就进行相应处理。

     还是直接上代码吧:

public class NIOServer {// 通道管理器private Selector selector;/** * 获得一个ServerSocket通道,并对该通道做一些初始化的工作 *  * @param port *            绑定的端口号 * @throws IOException */public void initServer(int port) throws IOException {// 获得一个ServerSocket通道ServerSocketChannel serverChannel = ServerSocketChannel.open();// 设置通道为非阻塞serverChannel.configureBlocking(false);// 将该通道对应的ServerSocket绑定到port端口serverChannel.socket().bind(new InetSocketAddress(port));// 获得一个通道管理器this.selector = Selector.open();// 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,// 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。serverChannel.register(selector, SelectionKey.OP_ACCEPT);}/** * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 *  * @throws IOException */public void listen() throws IOException {System.out.println("服务端启动成功!");// 轮询访问selectorwhile (true) {/* select()当注册的事件到达时,方法返回;否则,该方法会一直阻塞此方法执行处于阻塞模式的选择操作。仅在至少选择一个通道、调用此选择器的 wakeup 方法,或者当前的线程已中断(以先到者为准)后此方法才返回。也就是请求到达时唤醒*/int a = selector.select();// 获得selector中选中的项的迭代器,选中的项为注册的事件Iterator<?> ite = this.selector.selectedKeys().iterator();while (ite.hasNext()) {SelectionKey key = (SelectionKey) ite.next();// 删除已选的key,以防重复处理ite.remove();handler(key);}}}/** * 处理请求 *  * @param key * @throws IOException */public void handler(SelectionKey key) throws IOException {// 客户端请求连接事件if (key.isAcceptable()) {handlerAccept(key);// 获得了可读的事件} else if (key.isReadable()) {handelerRead(key);}}/** * 处理连接请求 *  * @param key * @throws IOException */public void handlerAccept(SelectionKey key) throws IOException {ServerSocketChannel server = (ServerSocketChannel) key.channel();// 获得和客户端连接的通道SocketChannel channel = server.accept();// 设置成非阻塞channel.configureBlocking(false);// 在这里可以给客户端发送信息哦System.out.println("新的客户端连接");// 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。channel.register(this.selector, SelectionKey.OP_READ);}/** * 处理读的事件 *  * @param key * @throws IOException */public void handelerRead(SelectionKey key) throws IOException {// 服务器可读取消息:得到事件发生的Socket通道SocketChannel channel = (SocketChannel) key.channel();// 创建读取的缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);int read = channel.read(buffer);if(read > 0){byte[] data = buffer.array();String msg = new String(data).trim();System.out.println("服务端收到信息:" + msg);//回写数据ByteBuffer outBuffer = ByteBuffer.wrap("这里是服务端返回的数据".getBytes());channel.write(outBuffer);// 将消息回送给客户端}else{System.out.println("客户端关闭");key.cancel();}}/** * 启动服务端测试 *  * @throws IOException */public static void main(String[] args) throws IOException {NIOServer server = new NIOServer();server.initServer(1234);server.listen();}}
NIO 1适用于连接较多且连接较短的情况,如:聊天服务器等