NIO群聊Demo

服务器端代码

复制代码
package com.pw.datastructure.nio.groupChat;

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

/**
 * @author pw
 * @version 1.0
 * @date 2022/9/13 9:29
 */
public class GroupChatServer {
    // 定义相关的属性
    /**
     * nio选择器
     */
    private Selector selector;
    /**
     * 做监听
     */
    private ServerSocketChannel listenChannel;
    /**
     * 端口
     */
    private static final int PORT = 6667;

    // 构造器,初始化任务
    public GroupChatServer() {
        try {
            // 1. 得到选择器
            selector = Selector.open();
            // 2. 创建socket服务通道
            listenChannel = ServerSocketChannel.open();
            // 3. 绑定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            // 4.设置listenChannel为非阻塞通道
            listenChannel.configureBlocking(false);
            // 5. 将listenChannel注册进selector 设置监听的事件是 ACCEPT (客户端连接时)
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 监听
    public void listen (){
        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();
                        // 监听到连接事件
                        if (key.isAcceptable()){
                            // 1. 拿到SocketChannel
                            SocketChannel sc = listenChannel.accept();
                            // 2. 将SocketChannel设置非阻塞
                            sc.configureBlocking(false);
                            // 3. SocketChannel注册
                            sc.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                            System.out.println(sc.getRemoteAddress() + " 上线");

                        }
                        // 监听到读取事件
                        if (key.isReadable()){
                            readData(key);
                        }
                        // 当前的key操作完成后,从集合中删除,防止多线程同时操作同一个key
                        iterator.remove();
                    }
                }else {
                    // 无事件处理
                    System.out.println("等待。。。。。。");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }
    }

    // 读取客户端消息
    public void readData (SelectionKey key){
        // 定义一个
        SocketChannel channel = null ;
        try {
            // 取到关联的channel
            channel = (SocketChannel) key.channel();
            // 创建一个缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = channel.read(buffer);
            // 判断是否读取到数据
            if (count > 0){
                // 读取到数据
                String msg = new String(buffer.array());
                // 服务端收到消息,输出消息
                System.out.println("form 客户端:" + msg);
                // 向其他的客户端转发消息
                sendInfoToOtherClients(msg ,channel);
            }

        }catch (IOException e){
            // 在读的过程中,client端断连了
            try {
                System.out.println(channel.getRemoteAddress() + "离线。。。");
                // 取消注册
                key.cancel();
                // 关闭通道
                channel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

    }

    // 转发消息给其他客户端(通道发消息)
    public void sendInfoToOtherClients (String msg, SocketChannel self) throws IOException{
        System.out.println("服务器转发消息中。。。");
        // 遍历所有注册到selector 上SocketChannel,并且排除自己
        for (SelectionKey item : selector.keys()) {
            // 通过key取出对应的SocketChannel
            Channel targetChannel = item.channel();

            if (targetChannel instanceof SocketChannel && targetChannel != self){
                // 转发
                ((SocketChannel) targetChannel).write(ByteBuffer.wrap(msg.getBytes()));
            }
        }

    }




    public static void main(String[] args) {

        //创建一个服务器对象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();

    }
}
复制代码

客户端代码

复制代码
package com.pw.datastructure.nio.groupChat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
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;

/**
 * @author pw
 * @version 1.0
 * @date 2022/9/13 10:20
 */
public class GroupChatClient {
    /**
     * 服务器的ip
     */
    private static final String HOST = "127.0.0.1";
    /**
     * 端口
     */
    private static final int SERVER_PORT = 6667;
    /**
     * 选择器
     */
    private Selector selector;
    /**
     * 通道
     */
    private SocketChannel socketChannel;
    /**
     * 客户端自己的地址
     */
    private String clientHost;

    public GroupChatClient (){
        try {
            selector = Selector.open();
            // 连接服务器
            socketChannel = socketChannel.open(new InetSocketAddress(HOST,SERVER_PORT));
            // 设置非阻塞
            socketChannel.configureBlocking(false);
            // 注册
            socketChannel.register(selector, SelectionKey.OP_READ);
            // 客户端自己的地址
            clientHost = socketChannel.getLocalAddress().toString().substring(1);
            System.out.println(clientHost + "客户端已准备。。。");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 向服务器发送消息
    public void sendInfo (String msg){
        msg = clientHost + "发送了:" + msg;
        try {
            socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    // 从服务器端读取消息
    public void readInfo (){
        try {
            int readCount = selector.select();
            if (readCount > 0){
                // 存在发生事件的通道
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    if (key.isReadable()){
                        // 得到相关的通道
                        SocketChannel sc =(SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        sc.read(buffer);
                        System.out.println(new String(buffer.array()));
                    }

                }
                iterator.remove();
            }
        }catch (IOException e){
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        // 1. 启动客户端
        GroupChatClient groupChatClient = new GroupChatClient();
        // 2. 启动一个线程
        new Thread(){
            @Override
            public void run() {
                while (true){
                    // 每隔三秒读取服务器端发送的数据
                    groupChatClient.readInfo();
                    try {
                        Thread.currentThread().sleep(3000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        // 客户端发送数据给服务器端
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()){
            String msg = scanner.nextLine();
            groupChatClient.sendInfo(msg);
        }
    }
}
复制代码

 

posted @   潘小伟  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示