java NIO实例学习

来源:互联网 发布:qt tcp客户端发送数据 编辑:程序博客网 时间:2024/05/19 13:09

ServerSocketChannel和SocketChannel

   在使用传统的ServerSocket和Socket的时候 很多时候程序是会阻塞的,比serverSocket.accept(),socket.getInputStream.read()

会阻塞accept(),除非等到客户端socket的连接或者异常中断,否则会一直等待下去。read()方法也是如此,除非在输入流中有了足够数据,否则该方法也会一直等待下去直到数据的到来。在ServerSocket和Socket方法中,服务器往往要为客户端分配一个线程,而每一个线程都有可能处于长时间的阻塞状态中,而过多的线程也会影响服务器的性能。因此在JDK1.4中引入了非阻塞的通信方式,这样使得服务器端只需要一个线程就能处理所有客户端Socket的请求。


通信实例

   上一篇的最后讲了在实例中需要用到的核心类。在这里就不再叙述了。

下面是一个该通信模式下的实例。首先建一个服务器端:


 public class NIOServer2{    /*标识数字*/    private  int flag = 0;    /*缓冲区大小*/    private  int BLOCK = 4096;    /*接受数据缓冲区*/    private  ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);    /*发送数据缓冲区*/    private  ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);    private  Selector selector;    public NIOServer2(int port) throws IOException {        // 打开服务器套接字通道        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();        // 服务器配置为非阻塞        serverSocketChannel.configureBlocking(false);        // 检索与此通道关联的服务器套接字        ServerSocket serverSocket = serverSocketChannel.socket();        // 进行服务的绑定        serverSocket.bind(new InetSocketAddress(port));        // 通过open()方法找到Selector        selector = Selector.open();        // 注册到selector,等待连接        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        System.out.println("Server Start----8888:");    }    // 监听    private void listen() throws IOException {        while (true) {            // 选择一组键,并且相应的通道已经打开            selector.select();            // 返回此选择器的已选择键集。            Set<SelectionKey> selectionKeys = selector.selectedKeys();            Iterator<SelectionKey> iterator = selectionKeys.iterator();            while (iterator.hasNext()) {                SelectionKey selectionKey = iterator.next();                iterator.remove();                handleKey(selectionKey);            }               }    }    // 处理请求    private void handleKey(SelectionKey selectionKey) throws IOException {        // 接受请求        ServerSocketChannel server = null;        SocketChannel client = null;        String receiveText;        String sendText;        int count=0;        // 测试此键的通道是否已准备好接受新的套接字连接。        if (selectionKey.isAcceptable()) {             System.out.println("服务器端的通道处于可连接状态。。。");            // 返回为之创建此键的通道。            server = (ServerSocketChannel) selectionKey.channel();            // 接受到此通道套接字的连接。            // 此方法返回的套接字通道(如果有)将处于阻塞模式。            client = server.accept();            // 配置为非阻塞            client.configureBlocking(false);            // 注册到selector,等待连接            client.register(selector, SelectionKey.OP_READ);        } else if (selectionKey.isReadable()) {            System.out.println("服务器端可读。。。");            // 返回为之创建此键的通道。            client = (SocketChannel) selectionKey.channel();            //将缓冲区清空以备下次读取            receivebuffer.clear();            //读取服务器发送来的数据到缓冲区中            count = client.read(receivebuffer);            if (count > 0) {                receiveText = new String( receivebuffer.array(),0,count);                System.out.println("服务器端接受客户端数据--:"+receiveText);                client.register(selector, SelectionKey.OP_WRITE);            }        } else if (selectionKey.isWritable()) {            System.out.println("服务器端可写。。。。");            //将缓冲区清空以备下次写入            sendbuffer.clear();            // 返回为之创建此键的通道。            client = (SocketChannel) selectionKey.channel();            sendText="message from server--" + flag++;            //向缓冲区中输入数据            sendbuffer.put(sendText.getBytes());            //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位            sendbuffer.flip();            //输出到通道            client.write(sendbuffer);            System.out.println("服务器端向客户端发送数据--:"+sendText);            client.register(selector, SelectionKey.OP_READ);        }    }    /**     * @param args     * @throws IOException     */    public static void main(String[] args) throws IOException {        // TODO Auto-generated method stub        int port = 1234;        NIOServer2 server = new NIOServer2(port);        server.listen();    }}
在该类中可以看到,建立服务器的方法是:首先打开服务器套接字通道,并进行服务绑定,然后注册到selector,等待客户端的连接。然后就是监听,实时监听通道的状态。监听的方法是   选择注册的一组键,该组键是一个集合,从中选出已选择的键集,然后通过迭代对每一个键进行处理。 

对已选择键进行处理:首先判断其正处的状态:连接就绪、读就绪、写就绪。对不同的状态作相应的处理


客户端:

public class NIOClient {    /*标识数字*/    private static int flag = 0;    /*缓冲区大小*/    private static int BLOCK = 4096;    /*接受数据缓冲区*/    private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);    /*发送数据缓冲区*/    private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);    /*服务器端地址*/    private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(            "localhost", 1234);    public static void main(String[] args) throws IOException {        // TODO Auto-generated method stub        // 打开socket通道        SocketChannel socketChannel = SocketChannel.open();        // 设置为非阻塞方式        socketChannel.configureBlocking(false);        // 打开选择器        Selector selector = Selector.open();        // 注册连接服务端socket动作        socketChannel.register(selector, SelectionKey.OP_CONNECT);        // 连接        socketChannel.connect(SERVER_ADDRESS);        // 分配缓冲区大小内存        Set<SelectionKey> selectionKeys;        Iterator<SelectionKey> iterator;        SelectionKey selectionKey;        SocketChannel client;        String receiveText;        String sendText;        int count=0;        while (true) {            //选择一组键,其相应的通道已为 I/O 操作准备就绪。            //此方法执行处于阻塞模式的选择操作。            selector.select();            //返回此选择器的已选择键集。            selectionKeys = selector.selectedKeys();            //System.out.println(selectionKeys.size());            iterator = selectionKeys.iterator();            while (iterator.hasNext()) {                selectionKey = iterator.next();                if (selectionKey.isConnectable()) {                    System.out.println("client connect");                    client = (SocketChannel) selectionKey.channel();                    // 判断此通道上是否正在进行连接操作。                    // 完成套接字通道的连接过程。                    if (client.isConnectionPending()) {                        client.finishConnect();                        System.out.println("完成连接!");                        sendbuffer.clear();                        sendbuffer.put("Hello,Server".getBytes());                        sendbuffer.flip();                        client.write(sendbuffer);                    }                    client.register(selector, SelectionKey.OP_READ);                } else if (selectionKey.isReadable()) {                    System.out.println("客户端可读。。。。");                    client = (SocketChannel) selectionKey.channel();                    //将缓冲区清空以备下次读取                    receivebuffer.clear();                    //读取服务器发送来的数据到缓冲区中                    count=client.read(receivebuffer);                    if(count>0){                        receiveText = new String( receivebuffer.array(),0,count);                        System.out.println("客户端接受服务器端数据--:"+receiveText);                        client.register(selector, SelectionKey.OP_WRITE);                    }                } else if (selectionKey.isWritable()) {                    System.out.println("客户端可写。。。");                    sendbuffer.clear();                    client = (SocketChannel) selectionKey.channel();                    sendText = "message from client--" + (flag++);                    sendbuffer.put(sendText.getBytes());                    //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位                    sendbuffer.flip();                    client.write(sendbuffer);                    System.out.println("客户端向服务器端发送数据--:"+sendText);                    client.register(selector, SelectionKey.OP_READ);                }            }            selectionKeys.clear();        }    }}

这是一个不断循环的通信实例。在具体的项目中根据自己的需求可进行相应的配置。

    

     

     

  


0 0
原创粉丝点击