zoukankan      html  css  js  c++  java
  • 基于NIO的Socket通信

    一、NIO模式的基本原理:

    服务端:

      首先,服务端打开一个通道(ServerSocketChannel),并向通道中注册一个通道调度器(Selector);然后向通道调度器注册感兴趣的事件SelectionKey(如:OP_ACCEPT),接着就可以使用通道调度器(Selector)轮询通道(ServerSocketChannel)上注册的事件,并进行相应的处理。

    客户端:

         客户端在请求与服务端通信时,也可以像服务器端一样注册感兴趣的事件(比服务端少了SelectionKey.OP_ACCEPT事件),并通过轮询来处理指定的事件,而不必阻塞。

    服务端和客户端各自维护一个通道调度器(Selector)对象,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。

    二、基于NIO的简单Socket通信实例

    服务端:

    package cn.nio;
    
    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;
    
    /**
     *
     * @author*/
    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
         */
        @SuppressWarnings("unchecked")
        public void listen() throws IOException {
            System.out.println("server start success");
            // 轮询访问selector
            while (true) {
                //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
                selector.select();
                // 获得selector中选中的项的迭代器,选中的项为注册的事件
                Iterator ite = this.selector.selectedKeys().iterator();
                while (ite.hasNext()) {
                    SelectionKey key = (SelectionKey) ite.next();
                    // 删除已选的key,以防重复处理
                    ite.remove();
                    // 客户端请求连接事件
                    if (key.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel) key
                                .channel();
                        // 获得和客户端连接的通道
                        SocketChannel channel = server.accept();
                        // 设置成非阻塞
                        channel.configureBlocking(false);
    
                        //在这里可以给客户端发送信息哦
                        channel.write(ByteBuffer.wrap(new String("send a message to client").getBytes()));
                        //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
                        channel.register(this.selector, SelectionKey.OP_READ);
                        
                        // 获得了可读的事件
                    } else if (key.isReadable()) {
                            read(key);
                    }
    
                }
    
            }
        }
        /**
         * 处理读取客户端发来的信息 的事件
         * @param key
         * @throws IOException 
         */
        public void read(SelectionKey key) throws IOException{
            // 服务器可读取消息:得到事件发生的Socket通道
            SocketChannel channel = (SocketChannel) key.channel();
            // 创建读取的缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(10);
            channel.read(buffer);
            byte[] data = buffer.array();
            String msg = new String(data).trim();
            System.out.println("received message from client:"+msg);
            ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
            channel.write(outBuffer);// 将消息回送给客户端
        }
        
        /**
         * 启动服务端测试
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
            NIOServer server = new NIOServer();
            server.initServer(8000);
            server.listen();
        }
    
    }

    客户端:

      1 package com.nio;
      2 
      3 import java.io.IOException;
      4 import java.net.InetSocketAddress;
      5 import java.nio.ByteBuffer;
      6 import java.nio.channels.SelectionKey;
      7 import java.nio.channels.Selector;
      8 import java.nio.channels.SocketChannel;
      9 import java.util.Iterator;
     10 
     11 /**
     12  *
     13  * @author 14  */
     15 public class NIOClient {
     16     //通道调度器
     17     private Selector selector;
     18 
     19     /**
     20      * 获得一个Socket通道,并对该通道做一些初始化的工作
     21      * @param ip 连接的服务器的ip
     22      * @param port  连接的服务器的端口号         
     23      * @throws IOException
     24      */
     25     public void initClient(String ip,int port) throws IOException {
     26         // 获得一个Socket通道
     27         SocketChannel channel = SocketChannel.open();
     28         // 设置通道为非阻塞
     29         channel.configureBlocking(false);
     30         // 获得一个通道调度器
     31         this.selector = Selector.open();
     32         
     33         // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
     34         //用channel.finishConnect();才能完成连接
     35         channel.connect(new InetSocketAddress(ip,port));
     36         //将通道调度器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
     37         channel.register(selector, SelectionKey.OP_CONNECT);
     38     }
     39 
     40     /**
     41      * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
     42      * @throws IOException
     43      */
     44     @SuppressWarnings("unchecked")
     45     public void listen() throws IOException {
     46         // 轮询访问selector
     47         while (true) {
     48             selector.select();
     49             // 获得selector中选中的项的迭代器
     50             Iterator ite = this.selector.selectedKeys().iterator();
     51             while (ite.hasNext()) {
     52                 SelectionKey key = (SelectionKey) ite.next();
     53                 // 删除已选的key,以防重复处理
     54                 ite.remove();
     55                 // 连接事件发生
     56                 if (key.isConnectable()) {
     57                     SocketChannel channel = (SocketChannel) key
     58                             .channel();
     59                     // 如果正在连接,则完成连接
     60                     if(channel.isConnectionPending()){
     61                         channel.finishConnect();
     62                         
     63                     }
     64                     // 设置成非阻塞
     65                     channel.configureBlocking(false);
     66 
     67                     //在这里可以给服务端发送信息哦
     68                     channel.write(ByteBuffer.wrap(new String("send a  message to server").getBytes()));
     69                     //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
     70                     channel.register(this.selector, SelectionKey.OP_READ);
     71                     
     72                     // 获得了可读的事件
     73                 } else if (key.isReadable()) {
     74                         read(key);
     75                 }
     76 
     77             }
     78 
     79         }
     80     }
     81     /**
     82      * 处理读取服务端发来的信息 的事件
     83      * @param key
     84      * @throws IOException 
     85      */
     86     public void read(SelectionKey key) throws IOException{
     87         //和服务端的read方法一样
     88     }
     89     
     90     
     91     /**
     92      * 启动客户端测试
     93      * @throws IOException 
     94      */
     95     public static void main(String[] args) throws IOException {
     96         NIOClient client = new NIOClient();
     97         client.initClient("localhost",8000);
     98         client.listen();
     99     }
    100 
    101 }
  • 相关阅读:
    stylish——一键为网页换肤,改变字体大小,去除广告
    css3实现可以计算的自适应布局——calc()
    用一个div模拟textarea并实现高度自适应
    关于background定位
    CSS3 :nth-of-type() 选择器
    css3圣诞雪景球
    ie6、7、8兼容部分css3
    html5 录制mp3音频,支持采样率和比特率设置
    html5 图片上传,支持图片预览、压缩、及进度显示,兼容IE6+及标准浏览器
    grunt配置太复杂?使用Qbuild进行文件合并、压缩、格式化等处理
  • 原文地址:https://www.cnblogs.com/cloudml/p/4698915.html
Copyright © 2011-2022 走看看