Java NIO实现多人聊天室

作者:RivenDong 时间:2022-04-05 09:03:17 

本文实例为大家分享了Java NIO实现多人聊天室的具体代码,供大家参考,具体内容如下

1. 服务器端代码

ChatServer类:


package nio.test.server;

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

public class ChatServer {

private static final int DEFAULT_PORT = 8888;
   private static final String QUIT = "quit";
   private static final int BUFFER = 1024;
   private ServerSocketChannel serverSocketChannel; //服务器端用于处理IO的通道
   private Selector selector;
   private ByteBuffer byteBufferReader = ByteBuffer.allocate(BUFFER); //用来读取消息
   private ByteBuffer byteBufferWriter = ByteBuffer.allocate(BUFFER); //用来转发消息时写入其他通道的缓冲区
   private Charset charset = Charset.forName("UTF-8"); //标准化编码解码
   private int port;

public ChatServer(){
       this(DEFAULT_PORT);
   }
   public ChatServer(int port){
       this.port = port;
   }
   private void start(){
       try {
           serverSocketChannel = ServerSocketChannel.open(); //创建服务器套接字通道
           serverSocketChannel.configureBlocking(false); //设置为非阻塞式调用
           serverSocketChannel.socket().bind(new InetSocketAddress(port));

selector = Selector.open(); //打开选择器
           serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
           System.out.println("启动服务器,监听端口:" + port + "...");

while (true) {
               selector.select();
               //selectionKeys包含了select()接收到的所有事件
               Set<SelectionKey> selectionKeys = selector.selectedKeys();
               for(SelectionKey key : selectionKeys){
                   //处理被触发的事件
                   handles(key);
               }
               selectionKeys.clear(); //把集合清空
           }
       } catch (IOException e) {
           e.printStackTrace();
       }finally {
           close(selector);//启到既关闭selector又关闭通道的作用
       }
   }

/**
    * 处理被触发的事件
    * @param key 每当通道被选择器注册时,都会创建一个选择键
    * @throws IOException
    */
   private void handles(SelectionKey key) throws IOException {
       // 触发 ACCEPT事件 --- 和客户端建立了连接
       if(key.isAcceptable()){
           ServerSocketChannel server = (ServerSocketChannel) key.channel();
           SocketChannel client = server.accept();
           client.configureBlocking(false);
           client.register(selector, SelectionKey.OP_READ);
           System.out.println(getClientName(client) + "已连接");
       }
       // 触发 READ事件 --- 客户端发送了消息给服务器端
       else if(key.isReadable()){
           SocketChannel client = (SocketChannel) key.channel();
           String fwdMsg = receive(client); //读取客户端消息
           if(fwdMsg.isEmpty()){ //客户端异常
               key.cancel(); //不再监视这个通道上的read事件
               selector.wakeup();
           }else {
               forwardMessage(client, fwdMsg); //转发客户端消息

// 检查用户是否退出
               if(readyToQuit(fwdMsg)){
                   key.cancel();//解除监听
                   selector.wakeup();
                   System.out.println(getClientName(client) + "已断开");
               }
           }
       }
   }

/**
    * 用于转发消息
    * @param client
    * @param fwdMsg
    * @throws IOException
    */
   private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException {
        for(SelectionKey key : selector.keys()){
            Channel connectedClient = key.channel();
            if(connectedClient instanceof ServerSocketChannel) continue;

if(key.isValid() && !client.equals(connectedClient)) {
                byteBufferWriter.clear();
                byteBufferWriter.put(charset.encode((getClientName(client)) + ":" + fwdMsg));
                byteBufferWriter.flip(); //写转读
                while(byteBufferWriter.hasRemaining()){
                    ((SocketChannel)connectedClient).write(byteBufferWriter);
                }
            }
        }
   }
   private String receive(SocketChannel client) throws IOException {
       byteBufferReader.clear();
       while(client.read(byteBufferReader) > 0);
       byteBufferReader.flip();
       return String.valueOf(charset.decode(byteBufferReader));

}
   private String getClientName(SocketChannel client){
       return "客户端[" + client.socket().getPort() + "]";
   }
   private boolean readyToQuit(String msg){
       return QUIT.equals(msg);
   }
   private void close(Closeable closeable){
       if(closeable != null){
           try {
               closeable.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

public static void main(String[] args) {
       ChatServer chatServer = new ChatServer(6666);
       chatServer.start();

}
}

2. 客户端代码

ChatClient类:


package nio.test.client;

import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Set;

public class ChatClient {
   private static final String DEFAULT_SERVER_HOST = "127.0.0.1";
   private static final int DEFAULT_SERVER_PORT = 6666;
   private static final String QUIT = "quit";
   private static final int BUFFER = 1024;

private String host;
   private int port;
   private SocketChannel client;
   private ByteBuffer byteBufferReader = ByteBuffer.allocate(BUFFER);
   private ByteBuffer byteBufferWriter = ByteBuffer.allocate(BUFFER);
   private Selector selector;
   private Charset charset = Charset.forName("UTF-8");

public ChatClient(){
       this(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
   }
   public ChatClient(String host, int port){
       this.host = host;
       this.port = port;
   }
   public boolean readyToQuit(String msg){
       return QUIT.equals(msg);
   }
   private void close(Closeable closeable){
       if(closeable != null){
           try {
               closeable.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
   private void start(){
       try {
           client = SocketChannel.open();
           client.configureBlocking(false);

selector = Selector.open();
           client.register(selector, SelectionKey.OP_CONNECT);
           client.connect(new InetSocketAddress(host, port));
           while(true){
               selector.select();
               Set<SelectionKey> selectionKeys = selector.selectedKeys();
               for(SelectionKey key : selectionKeys){
                   handles(key);
               }
               selectionKeys.clear();
           }
       } catch (IOException e) {
           e.printStackTrace();
       } catch (ClosedSelectorException e){
           //用户正常退出
       }finally {
           close(selector);
       }
   }
   private void handles(SelectionKey key) throws IOException {
       // CONNECT事件 连接就绪事件
       if(key.isConnectable()){
           SocketChannel client = (SocketChannel)key.channel();
           if(client.isConnectionPending()){//连接处于就绪状态
               client.finishConnect();
               // 处理用户的输入信息
               new Thread(new UserInputHandler(this)).start();
           }
           client.register(selector, SelectionKey.OP_READ);
       }
       // READ事件  服务器转发消息
       else if(key.isReadable()){
           SocketChannel client = (SocketChannel)key.channel();
           String msg = receive(client);
           if(msg.isEmpty()){
               // 服务器出现异常
               close(selector);
           }else{
               System.out.println(msg);
           }
       }
   }
   public void send(String msg) throws IOException {
       if(msg.isEmpty()){
           return ;
       }else{
           byteBufferWriter.clear();
           byteBufferWriter.put(charset.encode(msg));
           byteBufferWriter.flip();
           while(byteBufferWriter.hasRemaining()){
               client.write(byteBufferWriter);
           }
           //检查用户是否准备退出
           if(readyToQuit(msg)){
               close(selector);
           }
       }
   }
   private String receive(SocketChannel client) throws IOException {
       byteBufferReader.clear();
       while(client.read(byteBufferReader) > 0);
       byteBufferReader.flip();
       return String.valueOf(charset.decode(byteBufferReader));
   }
   public static void main(String[] args) {
       ChatClient chatClient = new ChatClient();
       chatClient.start();
   }
}

UserInputHandler类:


package nio.test.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class UserInputHandler implements Runnable{

private ChatClient chatclient;
   public UserInputHandler(ChatClient chatClient){
       this.chatclient = chatClient;
   }
   /**r
    *
    */
   @Override
   public void run() {
       try {
           //等待用户输入的消息
           BufferedReader consoleReader = new BufferedReader(
                   new InputStreamReader(System.in)
           );
           while(true){
               String input = consoleReader.readLine();
               //向服务器发送消息
               chatclient.send(input);
               //检查用户是否准备退出
               if(chatclient.readyToQuit(input)){
                   break;
               }
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

3. 执行效果截图

Java NIO实现多人聊天室

Java NIO实现多人聊天室

Java NIO实现多人聊天室

Java NIO实现多人聊天室

来源:https://blog.csdn.net/RivenDong/article/details/103327130

标签:Java,NIO,聊天室
0
投稿

猜你喜欢

  • 深入分析JAVA流程控制语句

    2023-11-20 10:48:32
  • 利用Intellij Idea连接远程服务器实现远程上传部署功能

    2022-05-31 13:15:54
  • mybatis中foreach嵌套if标签方式

    2023-11-20 23:11:05
  • 解决mybatis批量更新出现SQL报错问题

    2023-11-29 04:12:47
  • Spring Boot 在启动时进行配置文件加解密的方法详解

    2023-11-12 17:13:54
  • SpringBoot ApplicationContext接口深入分析

    2021-10-21 23:00:03
  • SpringCloud如何创建一个服务提供者provider

    2023-08-01 01:56:33
  • java实现FTP文件上传与文件下载

    2023-08-16 08:28:38
  • java web中图片验证码功能的简单实现方法

    2023-06-07 13:30:53
  • Java 反射类型Type的用法说明

    2023-01-23 04:15:44
  • Android中SeekBar和RatingBar用法实例分析

    2023-07-28 00:13:59
  • 15个顶级Java多线程面试题(附答案)

    2023-11-24 01:53:44
  • Java8 如何正确高效的使用并行流

    2021-06-01 20:29:19
  • 深入浅出重构Mybatis与Spring集成的SqlSessionFactoryBean(上)

    2021-12-01 18:27:49
  • Java后台线程操作示例【守护线程】

    2023-11-25 01:35:44
  • java安全编码指南之:Mutability可变性详解

    2023-11-11 06:30:24
  • Java 日期转换详解及实例代码

    2021-06-30 02:15:43
  • Android中bindService基本使用方法概述

    2023-08-05 19:18:56
  • Java使用Lettuce客户端在Redis在主从复制模式下命令执行的操作

    2023-11-28 21:38:19
  • 基于java集合中的一些易混淆的知识点(详解)

    2023-08-29 03:06:26
  • asp之家 软件编程 m.aspxhome.com