非阻塞通信(服务器端)

来源:互联网 发布:鞋店销售软件 编辑:程序博客网 时间:2024/06/05 14:13

一.非阻塞
1.非阻塞:线程执行方法时,如果操作没有就绪,就立即返回,不会一直等待操作就绪

2.java.nio提供非阻塞通信的类:
1)ServerSocketChannel:代替Server
2)SocketChannel:代替Socket
3)Selector:监控就绪事件
4)SelectionKey:注册事件的句柄

3.服务器端程序使用多线程处理阻塞IO,虽然可以响应多用户,但是存在局限性
1)JVM会给每个线程分配独立的资源,线程越多,系统开销越大
2)容易造成死锁
3)需要频繁转让CPU使用

4.完成多个任务可以使用多线程分别处理,也可以让单个线程以轮询的工作方式处理就绪的任务

二.创建非阻塞服务器端程序
对于服务区端造成阻塞的原因有:
1)客户端的连接
2)接收客户端的数据
3)响应客户端的数据
所以,可以在主线程中分别监控这些任务,当有任何一个就绪就执行

public class nio_demo2 {    private Selector selector = null;    private ServerSocketChannel serverSocketChannel = null;    private int port = 6666;    private Charset charset = Charset.forName("UTF-8");    public nio_demo2() throws IOException {        selector = Selector.open();        serverSocketChannel = ServerSocketChannel.open();        serverSocketChannel.socket().setReuseAddress(true);        serverSocketChannel.configureBlocking(false);        serverSocketChannel.socket().bind(new InetSocketAddress(port));        System.out.println("server start...");    }    public void service() throws IOException {        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        //select():监控注册的channel,当有需要处理的IO时,将对应的selectionKey加入到selected-key集合        while(selector.select()>0){            Set readyKey = selector.selectedKeys();            Iterator it = readyKey.iterator();            while(it.hasNext()){                SelectionKey key = null;                try{                    key = (SelectionKey)it.next();                    it.remove();                    if(key.isAcceptable()){                        ServerSocketChannel ssc = (ServerSocketChannel)key.channel();                        SocketChannel socketChannel = ssc.accept();                        System.out.println("client connection" +                                socketChannel.socket().getInetAddress()+":"                                +socketChannel.socket().getPort());                        //设置为非阻塞                        socketChannel.configureBlocking(false);                        ByteBuffer buffer = ByteBuffer.allocate(1024);                        //向selecctor注册读就绪事件和写就绪时件,同时关联buffer                        socketChannel.register(selector,                                SelectionKey.OP_READ|SelectionKey.OP_WRITE,buffer);                    }                    if(key.isReadable()){                        receive(key);                    }                    if(key.isWritable()){                        send(key);                    }                }catch (IOException e){                    e.printStackTrace();                    try{                        if(key!=null){                            key.cancel();                            key.channel().close();                        }                    }catch(Exception ex){                        ex.printStackTrace();                    }                }            }        }    }    //发送,处理写就绪事件    public void send(SelectionKey key) throws IOException {        //获得关联的附件        ByteBuffer buffer = (ByteBuffer)key.attachment();        SocketChannel socketChannel = (SocketChannel)key.channel();        buffer.flip();        String data = decode(buffer);        if(data.indexOf("\n")==-1){            return;        }        String outputData = data.substring(0,data.indexOf("\n")+1);        System.out.println(outputData);        ByteBuffer outputBuffer = encode("echo:"+outputData);        //发送一行数据        while(outputBuffer.hasRemaining()){            socketChannel.write(outputBuffer);        }        //删除已处理数据(读写操作同一个ByteBuffer)        ByteBuffer temp = encode(outputData);        buffer.position(temp.limit());        buffer.compact();        if(outputData.equals("end\n")){            key.cancel();            socketChannel.close();            System.out.println("close coonnection");        }    }    //接收,处理读就绪事件    public void receive(SelectionKey key) throws IOException {        //获取关联的buffer        ByteBuffer buffer = (ByteBuffer)key.attachment();        //获取关联的channel        SocketChannel socketChannel = (SocketChannel)key.channel();        ByteBuffer readBuffer = ByteBuffer.allocate(32);        socketChannel.read(readBuffer);        //flip:将极限设置为位置,将位置设置为0        readBuffer.flip();        buffer.limit(buffer.capacity());        buffer.put(readBuffer);    }    //解码    public String decode(ByteBuffer buffer){        CharBuffer charBuffer = charset.decode(buffer);        return charBuffer.toString();    }    //编码    public ByteBuffer encode(String str){        return charset.encode(str);    }    public static void main(String[] args) throws IOException {        nio_demo2 server = new nio_demo2();        server.service();    }}
原创粉丝点击