NIO

1. 服务端

public class EchoServer {
 
    private static int SOCKET_NUM = 55555;
 
    private static DateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
     
    /**
     * @param args
     */
    public static void main(String[] args) {
        new EchoServer().start();
    }
 
    public void start() {
        try {
            Selector selector = bindServer();  // 绑定服务端口,并定义一个事件选择器对象记录套接字通道的事件
             
            /* 通过此循环来遍例事件 */
            while (true) {
                log("Waiting events.");
                int n = selector.select(); // 查询事件如果一个事件都没有,这里就会阻塞
                log("Got events: " + n);
                 
                ByteBuffer echoBuffer = ByteBuffer.allocate(50); // 定义一个byte缓冲区来存储收发的数据
 
                /* 循环遍例所有产生的事件 */
                for (SelectionKey key : selector.selectedKeys()) {
                    SocketChannel sc;
                    selector.selectedKeys().remove(key);  // 将本此事件从迭带器中删除
                     
                    /* 如果产生的事件为接受客户端连接(当有客户端连接服务器的时候产生) */
                    if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
                         
                        ServerSocketChannel subssc = (ServerSocketChannel) key.channel(); // 定义一个服务器socket通道
                         
                        sc = subssc.accept(); // 将临时socket对象实例化为接收到的客户端的socket
                         
                        sc.configureBlocking(false); // 将客户端的socket设置为异步
                         
                        sc.register(selector, SelectionKey.OP_READ); // 将客户端的socket的读取事件注册到事件选择器中
                        
                        System.out.println("Got new client:" + sc);
                    }
                    /* 如果产生的事件为读取数据(当已连接的客户端向服务器发送数据的时候产生) */
                    else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
                         
                        sc = (SocketChannel) key.channel(); // 临时socket对象实例化为产生本事件的socket
                         
                        ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 定义一个用于存储byte数据的流对象,存储全部信息
                         
                        echoBuffer.clear(); // 先将客户端的数据清空
                         
                        try {
                            // 循环读取所有客户端数据到byte缓冲区中,当有数据的时候read函数返回数据长度
                            // NIO会自动的将缓冲区一次容纳不下的自动分段
                            int readInt = 0; // 为读取到数据的长度
                            while ((readInt = sc.read(echoBuffer)) > 0) {
                                // 如果获得数据长度比缓冲区大小小的话
                                if (readInt < echoBuffer.capacity()) {
                                     
                                    byte[] readByte = new byte[readInt]; // 建立一个临时byte数组,将齐长度设为获取的数据的长度
                                    // 循环向此临时数组中添加数据
                                    for (int i = 0; i < readInt; i++) {
                                        readByte[i] = echoBuffer.get(i);
                                    }
                                     
                                    bos.write(readByte); // 将此数据存入byte流中
                                }
                                // 否则就是获得数据长度等于缓冲区大小
                                else {                                    
                                    bos.write(echoBuffer.array()); // 将读取到的数据写入到byte流对象中
                                }
                            }
                            // 当循环结束时byte流中已经存储了客户端发送的所有byte数据
                            log("Recive msg: " + new String(bos.toByteArray()));
                        } catch (Exception e) {
                             
                            e.printStackTrace(); // 当客户端在读取数据操作执行之前断开连接会产生异常信息
                             
                            key.cancel(); // 将本socket的事件在选择器中删除
                            break;
                        }
                         
                        writeBack(sc, bos.toByteArray()); // 向客户端写入收到的数据
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 绑定服务端口,初始化整个服务
     * @throws IOException
     */
    private Selector bindServer() throws IOException {
        log("Start binding server socket:" + SOCKET_NUM);
         
        Selector selector = Selector.open(); // 定义一个事件选择器对象记录套接字通道的事件
 
        ServerSocketChannel ssc = ServerSocketChannel.open(); // 定义一个异步服务器socket对象
 
        ssc.configureBlocking(false);// 将此socket对象设置为异步
 
        ServerSocket ss = ssc.socket(); // 定义服务器socket对象-用来指定异步socket的监听端口等信息
 
        InetSocketAddress address = new InetSocketAddress(SOCKET_NUM); // 定义存放监听端口的对象
 
        ss.bind(address); // 将服务器与这个端口绑定
 
        ssc.register(selector, SelectionKey.OP_ACCEPT); // 将异步的服务器socket对象的接受客户端连接事件注册到selector对象内
 
        log("Binded socket at:" + SOCKET_NUM);
         
        return selector;
    }
     
    private boolean writeBack(SocketChannel sc, byte[] b) {
        ByteBuffer echoBuffer = ByteBuffer.allocate(b.length); // 建立这个byte对象的ByteBuffer
        echoBuffer.put(b); // 将数据存入 
         
        echoBuffer.flip(); // 将缓冲区复位以便于进行其他读写操作
        try {
            // 向客户端写入数据,数据为接受到数据
            sc.write(echoBuffer);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        System.out.println("Msg echo back: " + new String(echoBuffer.array()));
        return true;
    }
 
    private static void log(Object msg) {
        System.out.println("SERVER [" + dateFormatter.format(new Date()) + "]: " + msg);
    }
}

2. 客户端

public class EchoTestClient {
 
    private static int SOCKET_NUM = 55555;
 
    private static DateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        new EchoTestClient().start();
    }
 
    private void start() {
        SocketChannel client = null;
        try {
            Selector selector = Selector.open(); // 定义一个记录套接字通道事件的对象
 
            client = connect(selector);
 
            String a = "ABCD"; // 要发送的数据
 
            while (true) {
                ByteBuffer sendbuffer = ByteBuffer.allocate(40); // 定义用来存储发送数据的byte缓冲区
 
                ByteBuffer readBuffer = ByteBuffer.allocate(40); // 定义用于接收服务器返回的数据的缓冲区
 
                sendbuffer.put(a.getBytes()); // 将数据put进缓冲区
 
                sendbuffer.flip(); // 将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
 
                client.write(sendbuffer); // 向服务器发送数据
 
                log("Send to server: " + new String(sendbuffer.array()));
 
                // 利用循环来读取服务器发回的数据
                {
                    // 如果客户端连接没有打开就退出循环
                    if (!client.isOpen())
                        break;
                    // 此方法为查询是否有事件发生如果没有就阻塞,有的话返回事件数量
                    int shijian = selector.select(1000);
                    // 如果没有事件返回循环
                    if (shijian == 0) {
                        continue;
                    }
                    // 定义一个临时的客户端socket对象
                    SocketChannel sc;
                    // 遍例所有的事件
                    for (SelectionKey key : selector.selectedKeys()) {
                        // 删除本次事件
                        selector.selectedKeys().remove(key);
                        // 如果本事件的类型为read时,表示服务器向本客户端发送了数据
                        if (key.isReadable()) {
                            // 将临时客户端对象实例为本事件的socket对象
                            sc = (SocketChannel) key.channel();
                            // 定义一个用于存储所有服务器发送过来的数据
                            ByteArrayOutputStream bos = new ByteArrayOutputStream();
                            // 将缓冲区清空以备下次读取
                            readBuffer.clear();
                            // 此循环从本事件的客户端对象读取服务器发送来的数据到缓冲区中
                            while (sc.read(readBuffer) > 0) {
                                // 将本次读取的数据存到byte流中
                                bos.write(readBuffer.array());
                                // 将缓冲区清空以备下次读取
                                readBuffer.clear();
                            }
                            // 如果byte流中存有数据
                            if (bos.size() > 0) {
                                // 建立一个普通字节数组存取缓冲区的数据
                                byte[] b = bos.toByteArray();
 
                                log("Recive from server: " + new String(b));
                            }
                        }
                    }
                }
                Thread.sleep(2000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭客户端连接,此时服务器在read读取客户端信息的时候会返回-1
            if (client != null) {
                try {
                    client.close();
                } catch (IOException e) {
                }
                log("Connection closed!");
            }
        }
    }
 
    private SocketChannel connect(Selector selector) throws IOException {
 
        SocketAddress address = new InetSocketAddress("localhost", SOCKET_NUM); // 定义一个服务器地址的对象
 
        SocketChannel client = SocketChannel.open(address); // 定义异步客户端
 
        client.configureBlocking(false); // 将客户端设定为异步
 
        client.register(selector, SelectionKey.OP_READ); // 在轮讯对象中注册此客户端的读取事件(就是当服务器向此客户端发送数据的时候)
 
        return client;
    }
 
    private static void log(Object msg) {
        System.out.println("CLIENT [" + dateFormatter.format(new Date()) + "]: " + msg);
    }
}



原文地址:http://bbs.csdn.net/topics/380135133




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值