NIO案例-聊天室

NIO案例-聊天室

1. 聊天室服务端编写

package com.my.io.chat.server;
​
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
​
/**
 * @author zhupanlin
 * @version 1.0
 * @description: 聊天室的服务端
 * @date 2024/1/27 9:55
 */
public class ChatServer {
​
    public static void main(String[] args) {
        try {
            // 创建ServerSocketChannel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            // 绑定端口
            serverSocketChannel.bind(new InetSocketAddress(9001));
            // 得到selector
            Selector selector = Selector.open();
            // 将channel注册到selector上,并让selector对其接收的就绪状态感兴趣
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("聊天室服务器启动成功");
            // 轮询处理
            while (true){
                // 获得处于就绪状态的channel的总数
                int select = selector.select();
                if (select == 0){
                    continue;
                }
                // 获得就绪状态所有的selectionKeys集合
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 遍历selectionKeys集合
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    // 判断channel哪个操作处于就绪状态 channel被封装在selectionKey中
                    if (selectionKey.isAcceptable()){
                        // 说明有客户端连接,处理客户端的连接
                        handleAccept(selectionKey);
                    }else if (selectionKey.isReadable()){
                        // 说明有客户端向服务端写数据,处理客户端的写请求
                        handleRead(selectionKey);
                    }
                    //删除当前key
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
​
    /**
     * 处理客户端的连接请求
     * @param selectionKey
     */
    private static void handleAccept(SelectionKey selectionKey) {
        try {
            // 得到ServerSocketChannel
            ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
            // 获得selector
            Selector selector = selectionKey.selector();
            // 建立连接,获得socketChannel 此socketChannel与客户端的socketChannel为同一个
            SocketChannel socketChannel = channel.accept();
            // 将socketChannel设置成非阻塞
            socketChannel.configureBlocking(false);
            // 注册到selector中,让Selector对它的读操作感兴趣
            socketChannel.register(selector, SelectionKey.OP_READ);
            // 服务端向客户端回复一条消息
            String message = "欢迎进入聊天室";
            socketChannel.write(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
​
    }
​
    /**
     * 处理客户端写数据的请求--服务端处于读操作的就绪状态
     * 获得客户端写的数据,并且广播给其它所有的客户端
     * @param selectionKey
     */
    private static void handleRead(SelectionKey selectionKey) {
        try {
            // 获得SocketChannel
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            // 获得selector
            Selector selector = selectionKey.selector();
            // 创建Buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            // 读取数据到buffer中
            int len = 0;
            // 存放客户端发来的消息
            String message = "";
            while ((len = socketChannel.read(buffer)) > 0){
                buffer.flip();
                // 封装message
                message += new String(buffer.array(), 0, len, StandardCharsets.UTF_8);
            }
            // 把客户端发送的消息,广播给其它所有客户端
            if (message.length() > 0){
                // 服务端本地打印
                System.out.println(socketChannel.getRemoteAddress() + ":" + message);
                // 广播消息
                handleMessage(message, selectionKey);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
​
    /**
     * 广播消息给其它所有客户端
     * @param message 
     * @param selectionKey
     */
    private static void handleMessage(String message, SelectionKey selectionKey) {
        try {
            // 获得selector
            Selector selector = selectionKey.selector();
            // 获得selector上面所有注册的channel
            Set<SelectionKey> keys = selector.keys();
            // 遍历
            for (SelectionKey key : keys) {
                SelectableChannel channel = key.channel();
                // 不发给自己
                if (channel != selectionKey.channel() && channel instanceof SocketChannel){
                    // 发送消息
                    // 获得SocketChannel
                    SocketChannel socketChannel = (SocketChannel) channel;
                    // 发送消息
                    socketChannel.write(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
​
}

2.聊天室客户端编写

package com.my.io.chat.client;
​
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.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
​
/**
 * @author zhupanlin
 * @version 1.0
 * @description: 客户端程序
 *                  1. 发送数据到服务端
 *                  2. 接收服务端发来的消息
 * @date 2024/1/27 11:07
 */
public class ChatClient {
​
    /**
     * 启动客户端的方法
     * @param name 聊天室昵称
     */
    public void start(String name){
        try {
            // 创建channel
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9001));
            // 设置成非阻塞
            socketChannel.configureBlocking(false);
            // 获得selector
            Selector selector = Selector.open();
            // 注册channel到selector上,让selector对其读操作感兴趣,读取服务端发来的消息
            socketChannel.register(selector, SelectionKey.OP_READ);
            // 创建线程 专门负责发送消息
            new SendMessageThread(socketChannel, name).start();
            // 循环selector
            while (true){
                // 获得这次就绪状态的channel数量
                int select = selector.select();
                if (select == 0){
                    continue;
                }
                // 获得所有就绪状态的channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                // 遍历
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (key.isReadable()){
                        // 如果是读操作就绪,说明服务端广播数据了
                        // 获得channel
                        SocketChannel channel = (SocketChannel) key.channel();
                        // 创建buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        // 读取
                        int len = 0;
                        String message = "";
                        while ((len = channel.read(buffer)) > 0){
                            buffer.flip();
                            // 封装message
                            message += new String(buffer.array(), 0, len, StandardCharsets.UTF_8);
                            buffer.clear();
                        }
                        // 打印消息
                        System.out.println(message);
                        // 将channel再次注册到selector上,并让selector对读感兴趣
                        channel.register(selector, SelectionKey.OP_READ);
                    }
                    // 移除key
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
}
​
class SendMessageThread extends Thread{
    // 要发消息的socketChaneel
    private SocketChannel socketChannel;
    //自己的昵称
    private String name;
​
    public SendMessageThread(SocketChannel socketChannel, String name) {
        this.socketChannel = socketChannel;
        this.name = name;
    }
​
    @Override
    public void run() {
        // 控制台输入
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextLine()){
            String message = sc.nextLine();
            // 带上名字
            String sendMessage = name + ":" + message;
            // 发消息到channel
            try {
                if (message.length() > 0){
                    socketChannel.write(ByteBuffer.wrap(sendMessage.getBytes(StandardCharsets.UTF_8)));
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值