NIO 实例demo-Server

来源:互联网 发布:linux命令执行过程 编辑:程序博客网 时间:2024/06/08 07:11

NIO 实例demo-Server

下面是NIO编程的一个简单的demo ,总共包括四部分,Client,ClientHandler,Server和ServerHandler首先是Server端,NIO的server端的通信序列图如下图:

这里写图片描述

TimeServer

Server 的两部分代码如下:
Server主类比简单,设定端口号和serverHandler线程;

package MyTestNetty.Server;/** * Created by User on 2017/8/4. */public class TimeServer {    public static void main(String[] args) {        int port = 8080;        MultiplexerTimeServer timeServer = new MultiplexerTimeServer(port);        new Thread(timeServer,"myNIo server").start();    }}

MultiplexerTimeServer

第二部分是ServerHandler部分代码如下:

package MyTestNetty.Server;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;/** * Created by User on 2017/8/4. */public class MultiplexerTimeServer implements Runnable {    private Selector selector;    private ServerSocketChannel serverSocketChannel;    private volatile boolean stop;    public MultiplexerTimeServer(int port){        try {            selector = Selector.open();            serverSocketChannel = ServerSocketChannel.open();            serverSocketChannel.configureBlocking(false);            serverSocketChannel.socket().bind(new InetSocketAddress(port),1024);            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);            System.out.println("the time server start in port :"+ port);        } catch (IOException e) {            e.printStackTrace();        }    }    public void stop(){        this.stop =true;    }    @Override    public void run() {        while(!stop){//循环遍历selector,休眠时间为1S,当又处于就绪状态的CHannel时,selector将返回该channel的集合。通过对Channel集合的迭代,可进行网络异步读写操作            try {                selector.select(1000);                Set<SelectionKey> selectKeys = selector.selectedKeys();                Iterator<SelectionKey> it = selectKeys.iterator();                SelectionKey key = null;                while (it.hasNext()){                    key = it.next();                    it.remove();                    handleInput(key);                }            } catch (IOException e) {                e.printStackTrace();            }        }        //多路复用器关闭后,所注册在上面的Channel和Pipe等资源会被自动去注册并关闭,所以不需要重复释放资源        if(selector!=null){            try {                selector.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    private void handleInput(SelectionKey key) throws IOException {        if(key.isValid()){            if(key.isAcceptable()){//通过SelectionKey的操作位判断其事件的类型                ServerSocketChannel ssc =(ServerSocketChannel) key.channel();//                try {                    SocketChannel sc =ssc.accept();//创建SocketChannel实例                    sc.configureBlocking(false);                    sc.register(selector,SelectionKey.OP_READ);                } catch (IOException e) {                    e.printStackTrace();                }            }            if(key.isReadable()){//读取客户端请求                SocketChannel sc = (SocketChannel)key.channel();                ByteBuffer readbuffer = ByteBuffer.allocate(1024);                try {                    int readBytes = sc.read(readbuffer);                    if(readBytes>0){                        readbuffer.flip();                        byte[] bytes = new byte[readbuffer.remaining()];                        readbuffer.get(bytes);                        String body = new String(bytes,"UTF-8");                        System.out.println("Hello,glad to see you :"+body);                        String current = "QUERY TIME ORDER".equalsIgnoreCase(body)?new java.util.Date(System.currentTimeMillis()).toString():"BAD ORDER";                        doWrite(sc," server");                    }else{                        key.cancel();                        sc.close();                    }                } catch (IOException e) {//如果在catch中不添加key.cancel将会一直不停的抛出这个异常                    key.cancel();                    sc.socket().close();                    sc.close();                    System.out.println("execption handled");                    //e.printStackTrace();                }            }        }    }    private void doWrite(SocketChannel sc, String current) throws IOException {//        if(current!=null&&current.toString().trim().length()>0){            byte[] bytes = current.getBytes();            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);            writeBuffer.put(bytes);            writeBuffer.flip();            sc.write(writeBuffer);        }    }}

这一部分主要是包括监听客户端发送过来的请求,selector循环检查是否有channel就绪,如果有就绪的channel就将其的selectionKey选出进行IO读写,启动运行后可输出一下结果表示服务端启动成功

这里写图片描述

注意这一部分代码在90多行左右,这部分代码如果去掉,运行之后会不断的抛出异常:

catch (IOException e) {//如果在catch中不添加key.cancel将会一直不停的抛出这个异常                    key.cancel();                    sc.socket().close();                    sc.close();                    System.out.println("execption handled");                    //e.printStackTrace();                }

如果去掉之后会抛出一下异常:

这里写图片描述

这是因为客户端异常关闭后,服务器的选择器会获取到与客户端套接字对应的套接字通道SelectionKey,并且这个key的兴趣是OP_READ,执行从这个通道读取数据时,客户端已套接字已关闭,所以会出现“java.io.IOException: 远程主机强迫关闭了一个现有的连接”的错误。所以在catch中要进行处理,即取消当前key并关闭通道。

原创粉丝点击