java NIO 实例之多人聊天

关键抽象

  1.定义一个HashMap<String,SocketChannel>用户存储每个用户的管道。

  2.服务端监听read事件,获取消息后轮询hashmap发送消息给用户模型内的所有用户

  3.客户端简单read事件,读取聊天消息;发送消息给服务端

1.服务端代码

package com.leam.springboot.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.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;


/**
 * NIO服务端转发个所有客户端
 */
public class NioServer {
    private static Selector selector;

    /**
     * 开启一个服务端
     * 设置为非阻塞
     * 绑定端口号
     * backlog 处理的最大连接数,大于这个数直接拒绝
     */
    static {

        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(9999));
            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void server() throws IOException {
        Map<String, SocketChannel> clientMap = new HashMap<>(0);
        while (true) {
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            selectionKeys.forEach(selectionKey -> {
                try {

                    if (selectionKey.isAcceptable()) {
                        /**
                         * 服务器接收到客户端链接
                         * 保存接收到的客户端链接
                         */
                        ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
                        SocketChannel channel = server.accept();
                        channel.configureBlocking(false);
                        channel.register(selector, SelectionKey.OP_READ);
                        String key = UUID.randomUUID().toString();
                        clientMap.put(key, channel);
                        System.out.println(channel.getRemoteAddress() + "链接上服务器");
                    } else if (selectionKey.isReadable()) {
                        /**
                         * 读取客户端消息
                         * 转发到所有客户端
                         */
                        try {
                            SocketChannel channel = (SocketChannel) selectionKey.channel();
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            int len = channel.read(buffer);
                            if (len > 0) {
                                buffer.flip();
                                Charset charset = Charset.forName("UTF-8");
                                String receiveMsg = String.valueOf(charset.decode(buffer).array());
                                String key = null;
                                for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                                    if (entry.getValue() == channel) {
                                        key = entry.getKey();
                                        break;
                                    }
                                }
                                String sendMsg = key + ":" + receiveMsg;
                                System.out.println(sendMsg);
                                for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                                    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                                    writeBuffer.put(charset.encode(sendMsg));
                                    writeBuffer.flip();
                                    entry.getValue().write(writeBuffer);
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();

                            System.out.println("远程主机强迫关闭了一个现有的连接11111");
                        }
                    }

                } catch (Exception e) {
                    System.out.println("服务端出来异常");
                }
            });
            selectionKeys.clear();
        }
    }

    public static void main(String args[]) throws IOException {
        server();
    }

}

2.客户端代码

package com.leam.springboot.nio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 客户端注册
 */
public class NioClient {
    private static Selector selector;
    /**
     * 开启一个服务端
     * 设置为非阻塞
     * 连接到服务器
     */
    static {

        try {
            SocketChannel socketChannel=SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost",9999));
            selector=Selector.open();
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void client() throws IOException {
        Map<String,SocketChannel> clientMap=new HashMap<>(0);
        while (true) {
            selector.select();
            Set<SelectionKey> selectionKeys=selector.selectedKeys();
            selectionKeys.forEach( selectionKey -> {
                try {
                    if (selectionKey.isConnectable()) {
                        /**
                         * 客户端已连接
                         * 开启一个线程监听控制台输入
                         */
                        SocketChannel client = (SocketChannel) selectionKey.channel();
                        if (client.isConnectionPending()) {

                                client.finishConnect();

                        }
                        client.register(selector,SelectionKey.OP_READ);
//                        for(int i=0;i<5;i++){
                        ExecutorService executor = Executors.newSingleThreadExecutor();
                        System.out.println(client.getLocalAddress()+"连上了服务器");
                        ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                        executor.submit(()->{
                            try {
                                while (true) {
                                    writeBuffer.clear();
                                    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                                    String line = reader.readLine();
                                    writeBuffer.put(line.getBytes());
                                    writeBuffer.flip();
                                    client.write(writeBuffer);
                                }
                            }catch (Exception e) {
                                e.printStackTrace();
                            }
                        });
//                    }
                    } else if (selectionKey.isReadable()) {
                        /**
                         * 打印服务端消息
                         */
                        SocketChannel client = (SocketChannel) selectionKey.channel();
                        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        int len = client.read(readBuffer);
                        System.out.println(new String(readBuffer.array(),0,len));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            selectionKeys.clear();
        }

    }
    public static void main(String args[]) throws IOException {
         client();
    }
}

 

posted @ 2019-07-03 00:58  非尚  阅读(366)  评论(0)    收藏  举报