零基础java自学流程-Java语言高级539

本文介绍了如何使用Java NIO(New I/O)技术构建一个简单的聊天群组服务器,包括服务器端的端口监听、事件处理和消息转发,以及客户端的连接、消息发送与接收。通过实例展示了非阻塞I/O在实时通信中的应用。

NIO实现简单的聊天群

​



//服务器端
public class GroupChatSever {
    private final static int PORT = 6666;//监听端口
    private Selector selector;//选择器
    private ServerSocketChannel serverSocketChannel;

    public GroupChatSever(){
        try{
            selector = Selector.open();//开启选择器
            serverSocketChannel = ServerSocketChannel.open();//开启通道
            serverSocketChannel.configureBlocking(false);//将通道设为非阻塞状态
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));//通道绑定监听端口
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//将通道注册到选择器上,事件类型为接收
            listen();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //对端口进行监听
    public void listen(){
        try {
            while (true){
                //检查注册通道是否有事件发生,检查时长为2秒
                int count = selector.select(2000);
                if (count > 0){//如果注册通道有事件发生则进行处理
                    //获取所有发生事件的通道对应的SelectionKey
                    Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                    while (keyIterator.hasNext()){
                        SelectionKey key = keyIterator.next();
                        if (key.isAcceptable()){//判断该key对应的通道是否需进行接收操作
                            //虽然accept()方法是阻塞的,但是因为对通道进行过判断,
                            //可以确定是有客户端连接的,所以此时调用accept并不会阻塞
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            socketChannel.configureBlocking(false);
                            //接收后,将获取的客户端通道注册到选择器上,事件类型为读
                            socketChannel.register(selector, SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress() + "上线!");
                        }
                        if (key.isReadable()){//判断该key对应的通道是否需进行读操作
                            readFromClient(key);
                        }
                        //注意当处理完一个通道key时,需将它从迭代器中移除
                        keyIterator.remove();
                    }
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 读取客户端发来的消息
     * @param key 需读取的通道对应的SelectionKey
     */
    public void readFromClient(SelectionKey key){
        SocketChannel socketChannel = null;
        try{
            //通过SelectionKey获取对应通道
            socketChannel = (SocketChannel)key.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int read = socketChannel.read(byteBuffer);
            if (read > 0){
                String message = new String(byteBuffer.array());
                System.out.println("客户端: " + message);
                sendToOtherClient(message, socketChannel);
            }
        }catch (IOException e){
            //这里做了简化,将所有异常都当做是客户端断开连接触发的异常,实际项目中请不要这样做
            try{
                System.out.println(socketChannel.getRemoteAddress() + "下线");
                key.cancel();//将该SelectionKey撤销
                socketChannel.close();//再关闭对应通道
            }catch (IOException e2){
                e2.printStackTrace();
            }
        }
    }

    /**
     * 将客户端发送的消息转发到其他客户端
     * @param message 转发的消息
     * @param from 发送消息的客户端通道
     * @throws IOException
     */
    public void sendToOtherClient(String message, SocketChannel from) throws IOException{
        System.out.println("消息转发中......");
        for (SelectionKey key : selector.keys()){//遍历选择器中所有SelectionKey
            Channel channel = key.channel();//根据SelectionKey获取对应通道
            //排除掉发送消息的通道,将消息写入到其他客户端通道
            if (channel instanceof SocketChannel && channel != from){
                SocketChannel socketChannel = (SocketChannel)channel;
                ByteBuffer byteBuffer = ByteBuffer.wrap(message.getBytes());
                socketChannel.write(byteBuffer);
            }
        }
    }

    public static void main(String[] args) {
        GroupChatSever groupChatSever = new GroupChatSever();
    }
}
​
 
​


//客户端
public class GroupChatClient {
    private final static String SEVER_HOST = "127.0.0.1";//连接的客户端主机
    private final static int SEVER_PORT = 6666;//连接的客户端端口
    private Selector selector;//选择器
    private SocketChannel socketChannel;
    private String username;//储存客户端ip地址

    public GroupChatClient(){
        try {
            selector = Selector.open();//开启选择器
            socketChannel = SocketChannel.open(new InetSocketAddress(SEVER_HOST, SEVER_PORT));//开启通道
            socketChannel.configureBlocking(false);//将通道设为非阻塞
            socketChannel.register(selector, SelectionKey.OP_READ);//将通道注册在选择器上,事件类型为读
            username = socketChannel.getLocalAddress().toString().substring(1);//获取客户端ip地址
            String message = " 进入聊天群!";
            sendMessage(message);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //发送消息
    public void sendMessage(String message){
        message = username+": "+message;
        try{
            ByteBuffer byteBuffer = ByteBuffer.wrap(message.getBytes());
            socketChannel.write(byteBuffer);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //读取从服务器转发送过来的消息
    public void readMessage(){
        try{
            int read = selector.select();
            if (read > 0){
                Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                while (keyIterator.hasNext()){
                    SelectionKey key = keyIterator.next();
                    if (key.isReadable()){
                        SocketChannel socketChannel = (SocketChannel)key.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        socketChannel.read(byteBuffer);
                        System.out.println(new String(byteBuffer.array()));
                    }
                    keyIterator.remove();
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final GroupChatClient groupChatClient = new GroupChatClient();
        //客户端开启一个线程来监听是否有服务器转发来消息
        new Thread(){
            @Override
            public void run() {
                while (true){
                    groupChatClient.readMessage();
                    try {
                        Thread.currentThread().sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()){
            String message = scanner.nextLine();
            groupChatClient.sendMessage(message);
        }
    }
}



​

尚学堂给同学们带来全新的Java300集课程啦!java零基础小白自学Java必备优质教程_手把手图解学习Java,让学习成为一种享受_哔哩哔哩_bilibili尚学堂给同学们带来全新的Java300集课程啦本课程为Java300集2022版第一季,配合最新版的Java课程,所有视频重新录制,课件所有图形做了重新绘制和配色,图解学习Java,让学习成为一种享受本套教程专门为零基础学员而制,适合准备入行Java开发的零基础学员,视频中穿插多个实战项目。每一个知识点都讲解的通俗易懂,由浅入深。不仅适用于零基础的初学者,有经验的程序员也可做巩固学习。后续课https://www.bilibili.com/video/BV1qL411u7eE?spm_id_from=333.999.0.0

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值