JavaNIO Realization of Group Chat System

  • 2021-12-13 08:10:28
  • OfStack

In this paper, we share the specific code of Java NIO group chat system for your reference. The specific contents are as follows

The previous article introduces the three core components of NIO and compiles an demo example of BIO. This article uses NIO to write a small application example to consolidate and deepen the understanding of NIO.

Example requirements:

1) Write an NIO group chat system to realize simple data communication (non-blocking) between server and client
2) Realize multi-crowd chat
3) Server side: It can monitor users' online and offline, and realize message forwarding function
4) Client: Through channel, it can send messages to all other users without blocking, and at the same time, it can accept messages sent by other users (which are forwarded by the server)
5) Purpose: One step further to understand the NIO non-blocking network programming mechanism

Server-side code:


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
 
public class GroupChatServer {
    //定义属性
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT = 6667;
 
    //构造器
    //初始化工作
    public GroupChatServer() {
 
        try {
            //得到选择器
            selector = Selector.open();
            //ServerSocketChannel
            listenChannel =  ServerSocketChannel.open();
            //绑定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            //设置非阻塞模式
            listenChannel.configureBlocking(false);
            //将该listenChannel 注册到selector
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
 
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    //监听
    public void listen() {
 
        System.out.println("监听线程: " + Thread.currentThread().getName());
        try {
 
            //循环处理
            while (true) {
 
                int count = selector.select();
                if(count > 0) {//有事件处理
 
                    //遍历得到selectionKey 集合
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        //取出selectionkey
                        SelectionKey key = iterator.next();
 
                        //监听到accept
                        if(key.isAcceptable()) {
                            SocketChannel sc = listenChannel.accept();
                            sc.configureBlocking(false);
                            //将该 sc 注册到seletor
                            sc.register(selector, SelectionKey.OP_READ);
                            //提示
                            System.out.println(sc.getRemoteAddress() + " 上线 ");
                        }
                        if(key.isReadable()) { //通道发送read事件,即通道是可读的状态
                            //处理读 (专门写方法..)
                            readData(key);
                        }
                        //当前的key 删除,防止重复处理
                        iterator.remove();
                    }
 
                } else {
                    System.out.println("等待....");
                }
            }
 
        }catch (Exception e) {
            e.printStackTrace();
 
        }finally {
            //发生异常处理....
        }
    }
 
    //读取客户端消息
    private void readData(SelectionKey key) {
 
        //取到关联的channle
        SocketChannel channel = null;
 
        try {
           //得到channel
            channel = (SocketChannel) key.channel();
            //创建buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
 
            int count = channel.read(buffer);
            //根据count的值做处理
            if(count > 0) {
                //把缓存区的数据转成字符串
                String msg = new String(buffer.array());
                //输出该消息
                System.out.println("form 客户端: " + msg);
                //向其它的客户端转发消息(去掉自己), 专门写1个方法来处理
                sendInfoToOtherClients(msg, channel);
            }
 
        }catch (IOException e) {
            try {
                System.out.println(channel.getRemoteAddress() + " 离线了..");
                //取消注册
                key.cancel();
                //关闭通道
                channel.close();
            }catch (IOException e2) {
                e2.printStackTrace();;
            }
        }
    }
 
    //转发消息给其它客户(通道)
    private void sendInfoToOtherClients(String msg, SocketChannel self ) throws  IOException{
 
        System.out.println("服务器转发消息中...");
        System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());
        //遍历 所有注册到selector 上的 SocketChannel,并排除 self
        for(SelectionKey key: selector.keys()) {
 
            //通过 key  取出对应的 SocketChannel
            Channel targetChannel = key.channel();
 
            //排除自己
            if(targetChannel instanceof  SocketChannel && targetChannel != self) {
                //转型
                SocketChannel dest = (SocketChannel)targetChannel;
                //将msg 存储到buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //将buffer 的数据写入 通道
                dest.write(buffer);
            }
        }
    }
 
    public static void main(String[] args) {
        //创建服务器对象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }
}
 
//可以写1个Handler
class MyHandler {
    public void readData() {
    }
    public void sendInfoToOtherClients(){
    }
}

Client code:


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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
 
public class GroupChatClient {
 
    // Defining Related Attributes 
    private final String HOST = "127.0.0.1"; //  Server's ip
    private final int PORT = 6667; // Server port 
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;
 
    // Constructor ,  Complete the initialization work 
    public GroupChatClient() throws IOException {
 
        selector = Selector.open();
        // Connect to the server 
        socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));
        // Set non-blocking 
        socketChannel.configureBlocking(false);
        // Will channel  Register to selector
        socketChannel.register(selector, SelectionKey.OP_READ);
        // Get username
        username = socketChannel.getLocalAddress().toString().substring(1);
        System.out.println(username + " is ok...");
 
    }
 
    // Send a message to the server 
    public void sendInfo(String info) {
 
        info = username + "  Say: " + info;
        try {
            socketChannel.write(ByteBuffer.wrap(info.getBytes()));
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    // Read the message replied from the server 
    public void readInfo() {
 
        try {
 
            int readChannels = selector.select();
            if(readChannels > 0) {// There are available channels 
 
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
 
                    SelectionKey key = iterator.next();
                    if(key.isReadable()) {
                        // Get the related channels 
                       SocketChannel sc = (SocketChannel) key.channel();
                       // Get 1 A Buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        // Read 
                        sc.read(buffer);
                        // Convert the read buffer data into a string 
                        String msg = new String(buffer.array());
                        System.out.println(msg.trim());
                    }
                }
                iterator.remove(); // Delete the current selectionKey,  Prevent repetitive operations 
            } else {
                //System.out.println(" There is no available channel ...");
            }
 
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) throws Exception {
 
        // Start our client 
        GroupChatClient chatClient = new GroupChatClient();
 
        // Start 1 Threads ,  Every interval 3 Second, read the data sent from the server 
        new Thread() {
            public void run() {
 
                while (true) {
                    chatClient.readInfo();
                    try {
                        Thread.currentThread().sleep(3000);
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
 
        // Send data to the server 
        Scanner scanner = new Scanner(System.in);
 
        while (scanner.hasNextLine()) {
            String s = scanner.nextLine();
            chatClient.sendInfo(s);
        }
    }
}

Note: You must set the channel to non-blocking to register with Selector, otherwise report java. nio. channels. IllegalBlockingModeException error
Note: If you want to get the data from the server on the client, you also need to register on register (listen for read events)


Related articles: