主要利用Java NIO中TCP编程实现服务端和客户端通信,服务端接受客户端消息广播给其它客户端,客户端接受消息使用多线程实现。

服务端

服务端通过一个选择器来实现一个线程处理多个通道,实现IO多路复用。服务端代码:

public class Server {
    public static void main(String[] args) throws IOException {
        //启动服务端
        Server server = new Server();
        server.start();
    }
    //启动服务端
    public void start() throws IOException {
        //创建一个选择器
        Selector selector = Selector.open();
        //创建一个服务端通道ServerSocketChannel
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //给服务端绑定地址和端口
        ssc.socket().bind(new InetSocketAddress("127.0.0.1",8080));
        //设置为非阻塞
        ssc.configureBlocking(false);
        System.out.println("服务端启动成功");
        //将通道注册到选择器中,并设置选择器感兴趣的事情是连接到达
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        //死循环,一直遍历
        while(true){
            int num = selector.select();//获得当前选择器中有没有达到要求的通道
            if(num == 0){
                continue;
            }
            //当有多个或者一个通过有感兴趣的时候,使用迭代器遍历,获取key
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                //连接可通
                if(selectionKey.isAcceptable()){
          			
                    operatorAccept(ssc,selector);
                }else if(selectionKey.isReadable()){//连接可读
                    operatorRead(selectionKey,selector);
                }
                iterator.remove();//移出当前遍历
            }
        }
    }
    //当连接成功后,返回给客户端消息
    private void operatorAccept(ServerSocketChannel ssc,Selector selector) throws IOException {
        SocketChannel socketChannel = ssc.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector,SelectionKey.OP_READ);
        //回复消息
        socketChannel.write(Charset.forName("UTF-8").encode("您已进入聊天室,请注意隐式"));
    }
    //当一个客户端发送消息以后,将其消息广播给其它客户端
    private void operatorRead(SelectionKey selectionKey,Selector selector) throws IOException {
        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);//开辟一个1024字节大小的缓冲区用于读写消息
        StringBuilder stringBuilder = new StringBuilder();//使用StringBulder拼接消息
        int len;
        while((len = socketChannel.read(buffer)) != -1){
            if(len == 0){//当没有任何消息的时候就退出循环
                break;
            }
            buffer.flip();//读写模式切换
            stringBuilder.append(Charset.forName("UTF-8").decode(buffer));
        }
        String message = stringBuilder.toString();
        //Channel再次注册
        socketChannel.register(selector,SelectionKey.OP_READ);
        if(message.length() > 0){
            //输出消息
            System.out.println(message);
            //广播给其它客户端
            castOtherClient(message,selector,socketChannel);
        }
    }
    //将消息广播给其它客户端
    private void castOtherClient(String message,Selector selector,SocketChannel socketChannel) throws IOException {
        Set<SelectionKey> keys = selector.keys();
        for(SelectionKey key : keys){
            Channel channel = key.channel();
            //排除自己,给其它所有客户端发送消息
            if(channel instanceof SocketChannel && channel != socketChannel){
                ((SocketChannel)channel).write(
                        Charset.forName("UTF-8").encode(message));
            }
        }
    }
}

客户端

public class Client {
    private String name;//给每个客户端标记名字

    public Client(String name) {
        this.name = name;
    }

    public void start() throws IOException {
        //开启一个到服务端的客户端通道
        SocketChannel socketChannel = SocketChannel.open(
                new InetSocketAddress("127.0.0.1",8080));
        //接受服务端响应数据
        Selector selector = Selector.open();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        //创建线程进行处理
        new Thread(new ClientThread(selector)).start();

        //发送消息
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNextLine()){
            String msg = scanner.nextLine();
            if(msg.length() > 0){
                //发送消息到服务端
                socketChannel.write(Charset.forName("Utf-8").encode(name + " : "+msg));
            }
        }
    }
}
//使用实现Runable来实现多线程
public class ClientThread implements Runnable{
    private Selector selector;

    public ClientThread(Selector selector) {
        this.selector = selector;
    }

    @Override
    public void run() {
        while(true){
            int num = 0;
            try {
                num = selector.select();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(num == 0){
                continue;
            }
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                if(selectionKey.isReadable()){
                    try {
                        operatorRead(selectionKey,selector);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                iterator.remove();
            }
        }
    }
    //连接可读
    private void operatorRead(SelectionKey selectionKey,Selector selector) throws IOException {
        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        StringBuilder stringBuilder = new StringBuilder();
        int len;
        while((len = socketChannel.read(buffer)) != -1){
            if(len == 0){
                break;
            }
            buffer.flip();
            stringBuilder.append(Charset.forName("UTF-8").decode(buffer));
        }
        String message = stringBuilder.toString();
        //Channel再次注册
        socketChannel.register(selector,SelectionKey.OP_READ);
        if(message.length() > 0){
            System.out.println(message);
        }
    }
}

测试

public class ClientA {
    public static void main(String[] args) throws IOException {
        new Client("无涯子").start();
    }
}
public class ClientB {
    public static void main(String[] args) throws IOException {
        new Client("jack").start();
    }
}

java聊天室附完整代码 java nio聊天室_选择器

java聊天室附完整代码 java nio聊天室_服务端_02

java聊天室附完整代码 java nio聊天室_服务端_03