netty+protobuf实现聊天室

netty+protobuf实现聊天室

实现的功能

登陆,注册,私聊,群聊

代码

proto文件msg.proto

syntax = "proto2";
message Request{
    //消息类型 ,用户名,密码,消息,接收者(私聊,群聊)
    required int32 type = 1;//登录,心跳,私聊,群聊
    required string username = 2;
    optional string password = 3;
    optional string msg = 4;
    optional string receiver = 5;
}

你需要使用protoc.exe生成Msg类,这里不涉及介绍

Common包

public class Constant {
    public static final String LOGIN_SUCCESS = "LOGIN_OK";
    public static final String LOGIN_FAIL = "LOGIN_FAIL";
    public static final String REGISTER_SUCCESS = "REGISTER_OK";
    public static final String REGISTER_FAIL = "REGISTER_FAIL";
    public static final String ALL = "all";

    public static final String LOGIN = "login";//登录指令
    public static final String REGISTER = "register";//注册指令
    public static final String SEND = "send";//发送消息指令
}
public enum MsgType {
    //请求消息
    SERVICE_REQ(0),

    //响应消息
    SERVICE_RESP(1),

    //请求响应消息
    ONE_WAY(2),

    //握手请求消息
    LOGIN_REQ(3),

    //握手响应消息
    LOGIN_RESP(4),

    //心跳请求消息
    HEARTBEAT_REQ(5),

    //心跳响应消息
    HEARTBEAT_RESP(6),

    //发送消息请求
    SEND_REQ(7),
    //发送消息响应
    SEND_RESP(8),

    //注册请求
    REGISTER_REQ(9),
    //注册响应
    REGISTER_RESP(10),

    //接收响应
    RECEIVE_RESP(11);

    private int value;

    MsgType(int value) {
        this.value = value;
    }

    public int value(){
        return this.value;
    }
}

server

public class Account {
    //username pwd
    public static Map<String,String> account = new HashMap<>();

    //username channel
    public static Map<String, ChannelHandlerContext> channelMap = new HashMap<>();

    //channels
    public static List<ChannelHandlerContext> channels = new ArrayList<>();
}
public class Server {
    public static void main(String[] args) {
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
        ServerBootstrap server = new ServerBootstrap();
        server.group(boss,worker);
        server.channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 1024)	//设置TCP缓冲区
                    .option(ChannelOption.SO_RCVBUF, 32*1024)	// 设置接受数据的缓存大小
                    .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)	// 设置保持连接
                    .childOption(ChannelOption.SO_SNDBUF, 32*1024);
        server.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                ChannelPipeline pipeline = socketChannel.pipeline();

                pipeline.addLast(new ProtobufVarint32FrameDecoder());
                pipeline.addLast(new ProtobufDecoder(Msg.Request.getDefaultInstance()));

                pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
                pipeline.addLast(new ProtobufEncoder());

                pipeline.addLast(new ServerHandler());
            }
        });
            ChannelFuture future = server.bind(9990).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

@ChannelHandler.Sharable
public class ServerHandler extends SimpleChannelInboundHandler<Msg.Request> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Msg.Request request) throws Exception {
        System.out.println(request);
        int msgType = request.getType();
        //注册请求
        if(msgType== MsgType.REGISTER_REQ.value()){
            String username = request.getUsername();
            Msg.Request registerResp = null;
            if(Constant.ALL.equals(username)||Account.account.containsKey(username)){
                registerResp = buildRegisterResult(username,false);
            }else{
                String pwd = request.getPassword();
                Account.account.put(username,pwd);//注册的账户
                registerResp = buildRegisterResult(username,true);
            }
            ctx.writeAndFlush(registerResp);
        }
        //登录请求
        else if(msgType== MsgType.LOGIN_REQ.value()){
            String username = request.getUsername();
            String password = request.getPassword();
            Msg.Request loginResp = null;
            String accountPwd = Account.account.get(username);
            if(password.equals(accountPwd)){
                Account.channelMap.put(username,ctx);
                loginResp = buildLoginResult(true,username);
            }else {
                loginResp = buildLoginResult(false,username);
            }
            ctx.writeAndFlush(loginResp);
        }
        //发送请求
        else if(msgType== MsgType.SEND_REQ.value()){
            String username = request.getUsername();//发送者
            String receiver = request.getReceiver();//接收者
            String msg = request.getMsg();//发送消息
            //群聊 通知接收者
            if(Constant.ALL.equals(receiver)){
                Set set =  Account.channelMap.entrySet();
                Iterator it = set.iterator();
                while(it.hasNext()){
                    Map.Entry<String, ChannelHandlerContext> entry = (Map.Entry<String, ChannelHandlerContext>) it.next();
                    if(ctx!=entry.getValue()){
                        //除了自己不发,其他都发
                        Msg.Request allReceiveResp = buildReceiveResult(username,msg,entry.getKey());
                        entry.getValue().writeAndFlush(allReceiveResp);
                    }
                }
            }else{ //私聊 通知接收者
                //判断receiver是否存在
                if(!Account.account.containsKey(receiver)){
                    //通知发送者发送失败
                    Msg.Request sendFailResp = buildSendFailResult(receiver,"不存在该账户");
                    ctx.writeAndFlush(sendFailResp);
                    return;
                }
                ChannelHandlerContext channel = Account.channelMap.get(receiver);
                Msg.Request receiveResp = buildReceiveResult(username,msg,receiver);
                channel.writeAndFlush(receiveResp);
            }

            //通知发送者发送成功
            Msg.Request sendResp = buildSendResult(receiver);
            ctx.writeAndFlush(sendResp);
        }
    }

    private Msg.Request buildSendFailResult(String receiver, String failReason) {
        Msg.Request sendFailResp = Msg.Request.newBuilder().
                setType(MsgType.SEND_RESP.value())
                .setUsername("server").
                        setMsg("发送给"+receiver+"失败:"+failReason).build();

        return sendFailResp;
    }

    /**
     *
     * @param username 发送者
     * @param msg 内容
     * @param receiver 接收者
     * @return
     */
    private Msg.Request buildReceiveResult(String username, String msg, String receiver) {
        Msg.Request receiveResp = Msg.Request.newBuilder().
                setType(MsgType.RECEIVE_RESP.value()) //消息类型
                .setUsername(username). //发送者
                        setMsg(msg).  //消息内容
                        setReceiver(receiver). //接收者
                        build();
        return receiveResp;
    }

    private Msg.Request buildSendResult(String receiver) {
        Msg.Request sendResp = Msg.Request.newBuilder().
                setType(MsgType.SEND_RESP.value())
                        .setUsername("server").
                        setMsg("发送给"+receiver+"成功").build();
        return sendResp;
    }

    private Msg.Request buildLoginResult(boolean isSuccess, String username){
        String msg = isSuccess?Constant.LOGIN_SUCCESS:Constant.LOGIN_FAIL;
        Msg.Request loginResp = Msg.Request.newBuilder().
                setType(MsgType.LOGIN_RESP.value())
                .setUsername("server").
                        setMsg(msg).
                        setReceiver(username).
                        build();
        return loginResp;
    }

    private Msg.Request buildRegisterResult(String username, boolean isSuccess) {
        String msg = isSuccess? Constant.REGISTER_SUCCESS:Constant.REGISTER_FAIL;
        Msg.Request registerResp = Msg.Request.newBuilder().
                setType(MsgType.REGISTER_RESP.value())
                .setUsername("server").
                        setMsg(msg).
                        setReceiver(username).
                        build();
        return registerResp;
    }
}

client

public class Client {
    public static void main(String[] args) {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap client = new Bootstrap();
            client.group(group);
            client.channel(NioSocketChannel.class);
            client.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();

                    pipeline.addLast(new ProtobufVarint32FrameDecoder());//半包
                    pipeline.addLast(new ProtobufDecoder(Msg.Request.getDefaultInstance()));//解码的目标类

                    pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
                    pipeline.addLast(new ProtobufEncoder());

                    pipeline.addLast(new ClientHandler());

                }
            });
            ChannelFuture future =  client.connect(new InetSocketAddress("localhost",9990)).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }
}

@ChannelHandler.Sharable
public class ClientHandler extends SimpleChannelInboundHandler<Msg.Request> {
    private static String username;
    ExecutorService exec = Executors.newSingleThreadExecutor();
    @Override
    protected void channelRead0(final ChannelHandlerContext ctx, Msg.Request request) throws Exception {
        int msgType = request.getType();
        //注册响应
        if(msgType==MsgType.REGISTER_RESP.value()){
            if(Constant.REGISTER_SUCCESS.equals(request.getMsg())){
                System.out.println("register success");
                System.out.println("登录指令示范:login 用户名 密码");
            }else{
                System.out.println("register fail");
            }
        }
        //登录响应
        else if(msgType==MsgType.LOGIN_RESP.value()){
                if(Constant.LOGIN_SUCCESS.equals(request.getMsg())){
                    username = request.getReceiver();
                    System.out.println("login success");
                    System.out.println("发送指令示范:send 接收者(发送给所有人写all) 发送内容");
                }else{
                    System.out.println("login fail");
                }
        }
        //发送响应
        else if(msgType==MsgType.SEND_RESP.value()){
            System.out.println(request.getMsg());
        }
        //接收别人的消息
        else if(msgType==MsgType.RECEIVE_RESP.value()){
            String sendName = request.getUsername();
            String msg = request.getMsg();
            System.out.println("接收到"+sendName+"的消息:"+msg);
        }

        exec.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    handlerCommend(ctx);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("注册指令示范:register 用户名 密码");
        System.out.println("登录指令示范:login 用户名 密码");
        System.out.println("发送指令示范:send 接收者(发送给所有人写all) 发送内容");
        handlerCommend(ctx);
    }


    private void handlerCommend(ChannelHandlerContext ctx) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String commendStr = reader.readLine();
        String[] splits = commendStr.split(" ");
        String commend = splits[0];
        Msg.Request req = null;
        switch (commend){
            //注册
            case Constant.REGISTER:
                if(splits.length!=3){
                    System.out.println("指令错误,正确示范:register 用户名 密码");
                    handlerCommend(ctx);
                }
                req = buildRegisterReq(splits[1],splits[2]);
                ctx.writeAndFlush(req);
            break;
            //登录
            case Constant.LOGIN:
                if(splits.length!=3){
                    System.out.println("指令错误,正确示范:login 用户名 密码");
                    handlerCommend(ctx);
                }
                req = buildLoginReq(splits[1],splits[2]);
                ctx.writeAndFlush(req);
            break;

            case Constant.SEND:
                if(splits.length!=3){
                    System.out.println("指令错误,正确示范:send 接收者 内容");
                    handlerCommend(ctx);
                }
                if(username.equals(splits[1])){
                    System.out.println("指令错误:接收者和发送者一样");
                    handlerCommend(ctx);
                }
                req = buildSendMsg(splits[1],splits[2]);
                ctx.writeAndFlush(req);
            break;

            default:
                System.out.println("指令错误");
                System.out.println("注册指令示范:register 用户名 密码");
                System.out.println("登录指令示范:login 用户名 密码");
                System.out.println("发送指令示范:send 接收者(发送给所有人写all) 发送内容");
                handlerCommend(ctx);
            break;
        }
    }

    private Msg.Request buildSendMsg(String receiver, String content) {
        Msg.Request req = Msg.Request.newBuilder().
                setUsername(username).//发送者
                setType(MsgType.SEND_REQ.value()).
                setReceiver(receiver).//接收者
                setMsg(content).
                build();
        return req;
    }

    private Msg.Request buildLoginReq(String username, String pwd) {
        Msg.Request req = Msg.Request.newBuilder().
                setType(MsgType.LOGIN_REQ.value()).
                setUsername(username).
                setPassword(pwd).
                setMsg("login").
                setReceiver("server").
                build();
        return req;
    }

    private Msg.Request buildRegisterReq(String username, String pwd) {
        Msg.Request req = Msg.Request.newBuilder().
                setType(MsgType.REGISTER_REQ.value()).
                setUsername(username).
                setPassword(pwd).
                setMsg("register").
                setReceiver("server").
                build();
        return req;
    }

}

你可能感兴趣的:(netty)