Netty基础,Netty实现群聊系统

NIO群聊系统

这里面的知识比较全面,用到了我们之前学习的三大组件,首先我先来给大家介绍本系统的功能

服务端功能

最基本的当然是注册功能,也就是将serverSocketChannel注册进Selector,Selector负责调度事件

监听、读取客户端发来的消息

收到客户端的信息后将其转发到其它客户端,完成群聊功能

客户端功能

连接服务端,注册进Selector

读写消息

以上就是群聊系统最基本的功能,我们直接上代码,每句话的含义我都标在它的顶上了

服务端源码

package com.hecl.designmodels.NIO.GroupChat;


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

public class GroupChatServer {
    //定义一个选择器对象
    private Selector selector;
    //定义一个ServerSocketChannel对象
    private ServerSocketChannel serverSocketChannel;
    //养成习惯,对于这种不变的属性我们使用static final 来定义,且单词为全大写
    private static final int PORT = 7000;

    public GroupChatServer() {
        try{
            //获取选择器
            selector = Selector.open();
            //获取ServerSocketChannel
            serverSocketChannel = ServerSocketChannel.open();
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //将serverSocketChannel注册进selector
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //监听方法
    public void listen(){
        try{
            while(true){
                int count = selector.select();
                //如果count大于0就说明有事件发生
                if(count > 0){
                    //selector.selectedKeys()获取一个Set<SelectionKey>集合
                    //遍历SelectionKey
                    Iterator<SelectionKey> iterator =  selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        //获取目前遍历到的SelectionKey对象
                        SelectionKey key = iterator.next();
                        //有新连接创建
                        if(key.isAcceptable()){
                            //获取连接到服务器的SocketChannel
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            //将该通道设置为非阻塞
                            socketChannel.configureBlocking(false);
                            //注册进selector
                            socketChannel.register(selector,SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress() + ": 已登录");
                        }
                        //发生有数据发送过来触发读事件
                        if(key.isReadable()){
                        	//调用读取数据的方法
                            readMsg(key);
                        }
                        iterator.remove();
                    }
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //读取数据方法
    public void readMsg(SelectionKey selectionKey){
        SocketChannel socketChannel = null;
        try {
            //通过key获取与之关联的SocketChannel
            socketChannel = (SocketChannel) selectionKey.channel();
            //创建缓冲区去接收通道内的数据
            ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
            //得到通道内的数据
            socketChannel.read(byteBuffer);
            //在控制台打印消息
            System.out.println("from " + socketChannel.getRemoteAddress() + "客户端 :" + new String(byteBuffer.array()));
            //转发消息
            sendMsgToOtherClients(new String(byteBuffer.array()),socketChannel);
        }catch (IOException e){
            try{
                System.out.println(socketChannel.getRemoteAddress() + "下线");
                //消毁selectionKey
                selectionKey.cancel();
                //销毁socketChannel
                socketChannel.close();
            }catch (Exception exception){
                exception.printStackTrace();
            }
        }
    }

    //向其他客户端转发消息,不包括自己
    public void sendMsgToOtherClients(String msg,SocketChannel socketChannel){
        try {
            //获取所有的SelectionKey
            for(SelectionKey OtherKey : selector.keys()){
                //获取对应的socketChannel
                Channel OtherChannel = OtherKey.channel();
                //排除自身
                if(OtherChannel instanceof SocketChannel && socketChannel != OtherChannel){
                    //向下转型
                    SocketChannel OtherSocketChannel = (SocketChannel) OtherChannel;
                    //创建缓冲区
                    ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
                    //将缓冲区的数据写入通道
                    OtherSocketChannel.write(byteBuffer);
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
    	//启动服务端
        GroupChatServer groupChatServer = new GroupChatServer();
        //调用监听方法
        groupChatServer.listen();
    }
}

客户端源码

package com.hecl.designmodels.NIO.GroupChat;

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import designModels.design_mode_07_BridgePattern.Pay;

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 {
    //定义需要连接服务器的地址与端口号
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 7000;
    //定义选择器对象
    private Selector selector;
    //定义SocketChannel对象
    private SocketChannel socketChannel;
    //登录名
    private String username;

    public GroupChatClient(String username){
        try {
            //获取一个SocketChannel对象,并连接服务器
            socketChannel = SocketChannel.open(new InetSocketAddress(HOST,PORT));
            //得到选择器
            selector = Selector.open();
            //设置非阻塞
            socketChannel.configureBlocking(false);
            //将通道注册进selector
            socketChannel.register(selector, SelectionKey.OP_READ);
            //得到登录名称
            this.username = username;
            String s = username + " 加入群聊";
            System.out.println(s);
            socketChannel.write(ByteBuffer.wrap(s.getBytes()));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public void sendMsg(String msg){
        //数据拼接形成最终发送的消息
        String info = username + " say : " + msg;
        try{
            //将缓冲区内的数据写入通道
            socketChannel.write(ByteBuffer.wrap(info.getBytes()));
            System.out.println("I say : " + msg);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //参照服务端读取数据代码
    public void readMsg(){
        try{
            int count = selector.select();
            if(count>0){
                Set<SelectionKey> selectionKeySet = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeySet.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isReadable()){
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
                        socketChannel.read(byteBuffer);
                        String msg = new String(byteBuffer.array());
                        System.out.println(msg);
                    }
                    //必须要移除,要不然收不到其它客户端的消息
                    iterator.remove();
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Scanner scannerName = new Scanner(System.in);
        //启动服务
        GroupChatClient groupChatClient = new GroupChatClient(scannerName.nextLine());

        //新建一个线程循环调用读取数据的方法
        new Thread() {
            public void run(){
                while(true) {
                    groupChatClient.readMsg();
                    try {
                        //休息2s
                        Thread.sleep(2000);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        //获取输入消息
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()){
            //获取输入消息并调用sendMsg(String msg)
            groupChatClient.sendMsg(scanner.nextLine());
        }
    }

}

我们来看看最终实现效果

服务器端
在这里插入图片描述
客户端1
在这里插入图片描述
客户端2
在这里插入图片描述
客户端3
在这里插入图片描述
在这里插入图片描述
前面发送的消息,后加入的客户端获取不到,这就是通过NIO实现的简单群聊啦

基础都打得差不多了,下一篇我们就应该正式进入Netty的章节啦,赞赞赞赞赞

完成:2021/3/28 20:20 ALiangX

转载请标注原作者,谢谢你们的支持,能给个小心心吗?
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉淀顶峰相见的PET

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值