Java NIO 实现多人聊天室

服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class ChatServer {
    
    

    public void serverDemo() throws IOException {
    
    
//        创建Selector选择器
        Selector selector = Selector.open();
//        创建ServerSocketChannel通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//        绑定端口
        serverSocketChannel.bind(new InetSocketAddress(8000));
//        创建ByteBuffer缓存区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
//        设置非阻塞IO
        serverSocketChannel.configureBlocking(false);
//        将serverSocketChannel通道注册到Selector选择器中
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("——————————服务器启动成功!——————————");
//        遍历可用连接
        for (;;){
    
    
//            查看有多少连接
            int select = selector.select();
//            若是没有连接跳过循环
            if (select ==0){
    
    
                continue;
            }
//            获取可用的连接集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
//            遍历连接集合
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
            while (selectionKeyIterator.hasNext()){
    
    
//                从集合中获取单个连接
                SelectionKey selectionKey = selectionKeyIterator.next();
//                删除集合
                selectionKeyIterator.remove();
//                判断该链接的状态
//                如果是accept状态的
                if (selectionKey.isAcceptable()){
    
    
                    acceptOperator(serverSocketChannel,selector);
                }
//                如果是只读状态的
                if (selectionKey.isReadable()){
    
    
                    readOperator(selector,selectionKey);
                }
            }
        }
    }
//    读取客户端发来的消息
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
    
    
//        创建socketChannel通道
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//        创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
//        切换非阻塞状态
        socketChannel.configureBlocking(false);
//        将通道数据写入到缓存中
        int read = socketChannel.read(buffer);
//        遍历输出客户端发来的消息
        String message = "";
        if (read !=-1){
    
    
            buffer.flip();
            message+= Charset.forName("UTF-8").decode(buffer);
        }
//        将通道注册到selector选择器中
        socketChannel.register(selector,SelectionKey.OP_READ);
//        广播给其他客户端
        if (message.length()>0){
    
    
            System.out.print(message);
            castOtherClient(message,selector,socketChannel);
        }
    }
//    广播客户端发来的信息,发给每个客户端
    private void castOtherClient(String message, Selector selector, SocketChannel socketChannel) throws IOException {
    
    
//        获取所有接入的信息
        Set<SelectionKey> keys = selector.keys();
//        循环获取每个channel,广播信息
        for (SelectionKey key : keys) {
    
    
//            获取每个channel
            Channel channel = key.channel();
            if (channel instanceof SocketChannel && channel != socketChannel){
    
    
//                给每个channel发送信息
                ((SocketChannel) channel).write(Charset.forName("UTF-8").encode(message));
            }
        }
    }
//    
    private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
    
    
//        接入状态,创建socketChannel
        SocketChannel socketChannel = serverSocketChannel.accept();
//        把socketChannel通道,设置成非阻塞模式
        socketChannel.configureBlocking(false);
//        把socketChannel注册到选择器中
        socketChannel.register(selector,SelectionKey.OP_READ);
//        给客户端回复信息
        socketChannel.write(Charset.forName("UTF-8").encode("您已进入聊天室,请注意你的隐私,防止隐私泄露"+"\n"));
    }

    public static void main(String[] args) throws IOException {
    
    
        new ChatServer().serverDemo();
    }
}

客户端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;

public class ChatClient {
    
    

    public void chatClient(String name) throws IOException {
    
    
//        创建SocketChannel
        SocketChannel socketChannel = SocketChannel
                .open(new InetSocketAddress("127.0.0.1",8000));
//        创建选择器
        Selector selector = Selector.open();
//        切换非阻塞模式
        socketChannel.configureBlocking(false);
//        将通道注册进选择器
        socketChannel.register(selector, SelectionKey.OP_READ);
//        创建线程,返回其他客户端的消息
        new Thread(new ChatThread(selector)).start();
//        获取控制台的信息
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
    
    
            String s = scanner.nextLine();
            if (s.length()>0){
    
    
                socketChannel.write(Charset.forName("UTF-8").encode(name+" : "+s+"\n"));

            }
        }
    }
}

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class ChatThread implements Runnable{
    
    

    private Selector selector;
   public ChatThread(Selector selector){
    
    
        this.selector=selector;
    }
    @Override
    public void run() {
    
    
        try{
    
    
//        遍历可用连接
            for (;;){
    
    
//            查看有多少连接
                int select = selector.select();
//            若是没有连接跳过循环
                if (select ==0){
    
    
                    continue;
                }
//            获取可用的连接集合
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
//            遍历连接集合
                Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
                while (selectionKeyIterator.hasNext()){
    
    
//                从集合中获取单个连接
                    SelectionKey selectionKey = selectionKeyIterator.next();
//                删除集合
                    selectionKeyIterator.remove();
//                判断该链接的状态
//                如果是只读状态的
                    if (selectionKey.isReadable()){
    
    
                        readOperator(selector,selectionKey);
                    }
                }
            }
        }catch (Exception e){
    
    

        }

    }
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
    
    
//        创建socketChannel通道
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//        创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
//        切换非阻塞状态
        socketChannel.configureBlocking(false);
//        将通道数据写入到缓存中
        int read = socketChannel.read(buffer);
//        遍历输出客户端发来的消息
        String message = "";
        if (read !=-1){
    
    
            buffer.flip();
            message+= Charset.forName("UTF-8").decode(buffer);
        }
//        将通道注册到selector选择器中
        socketChannel.register(selector,SelectionKey.OP_READ);
//        广播给其他客户端
        if (message.length()>0){
    
    
            System.out.print(message);
        }
    }
}

下面模拟创建3个用户(A,B,C)进行聊天

import java.io.IOException;

public class AClient {
    
    

    public static void main(String[] args) throws IOException {
    
    
        new ChatClient().chatClient("A");
    }
}
import java.io.IOException;
public class BClient {
    
    
    public static void main(String[] args) throws IOException {
    
    
        new ChatClient().chatClient("B");
    }
}
import java.io.IOException;

public class CClient {
    
    
    public static void main(String[] args) throws IOException {
    
    
        new ChatClient().chatClient("C");
    }
}

测试

顺序:

  • 先启动服务端
  • 在启动三个客户端

假如在A客户输入一句话,回车;可以看到B,C,服务端同样接收到了A发送的信息,同理,B,C发消息一样可以被其他用户和服务端看到。

A用户
在这里插入图片描述
B 用户
在这里插入图片描述
服务端
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/CSDN_java1005/article/details/120964606