Netty实现消息推送以及内部心跳机制

来源:互联网 发布:csgo优化 编辑:程序博客网 时间:2024/06/05 19:16

Netty实现消息推送以及内部心跳机制

通讯信息的基类,需要实现序列化,定义了信息的类型和客户端ID,方便进行管理

public abstract class BaseMsg implements Serializable{  private static final long serialVersionUID = 1L;  private MsgType msgType;  private String clientID;    public BaseMsg() {this.clientID = Constants.getClientID();}  public MsgType getMsgType() {return msgType;}public void setMsgType(MsgType msgType) {this.msgType = msgType;}public String getClientID() {return clientID;}public void setClientID(String clientID) {this.clientID = clientID;}}

请求参数

public class AskParams implements Serializable {    private static final long serialVersionUID = 1L;    private String auth;     public String getAuth() {        return auth;    }     public void setAuth(String auth) {        this.auth = auth;    }}

常量类,存放客户端ID

public class Constants {private static String clientID;public static String getClientID() {return clientID;}public static void setClientID(String clientID) {Constants.clientID = clientID;}}

客户端登录信息,有用户名和密码

public class LoginMsg extends BaseMsg{String username;String password;public LoginMsg() {super();setMsgType(MsgType.LOGIN);}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}

枚举类,主要包含消息协议类型的定义

public enum MsgType { PING,ASK,REPLY,LOGIN;}

管理客户端的Channel,封装在map中,便于管理,可以进行添加,移除和获取

public class NettyChannelMap {private static Map<String, SocketChannel> map = new ConcurrentHashMap<String, SocketChannel>();  public static void add(String clientId,SocketChannel socketChannel){        map.put(clientId,socketChannel);    }    public static Channel get(String clientId){       return map.get(clientId);    }    public static void remove(SocketChannel socketChannel){        for (Map.Entry entry:map.entrySet()){            if (entry.getValue()==socketChannel){                map.remove(entry.getKey());            }        }    }}

心跳包传输数据类型

public class PingMsg extends BaseMsg{public PingMsg() {super();setMsgType(MsgType.PING);}}

消息回应类

public class ReplyMsg extends BaseMsg {    public ReplyMsg() {        super();        setMsgType(MsgType.REPLY);    }    private ReplyBody body;    public ReplyBody getBody() {        return body;    }    public void setBody(ReplyBody body) {        this.body = body;    }}

实现序列化的信息主体

public class ReplyBody implements Serializable {    private static final long serialVersionUID = 1L;}

客户端回应消息

public class ReplyClientBody extends ReplyBody {    private String clientInfo;     public ReplyClientBody(String clientInfo) {        this.clientInfo = clientInfo;    }     public String getClientInfo() {        return clientInfo;    }     public void setClientInfo(String clientInfo) {        this.clientInfo = clientInfo;    }}

服务端回应消息

public class ReplyServerBody extends ReplyBody{  private String serverInfo;    public ReplyServerBody(String serverInfo) {        this.serverInfo = serverInfo;    }    public String getServerInfo() {        return serverInfo;    }    public void setServerInfo(String serverInfo) {        this.serverInfo = serverInfo;    }}

客户端的处理类,主要是对于消息类型的判断处理和心跳处理机制的实现

public class NettyClientHandler extends SimpleChannelInboundHandler<BaseMsg>{   @Override    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {        if (evt instanceof IdleStateEvent) {            IdleStateEvent e = (IdleStateEvent) evt;            switch (e.state()) {                case WRITER_IDLE:                    PingMsg pingMsg=new PingMsg();                    ctx.writeAndFlush(pingMsg);                    System.out.println("send ping to server----------");                    break;                default:                    break;            }        }    }    protected void messageReceived(ChannelHandlerContext channelHandlerContext, BaseMsg baseMsg) throws Exception {        MsgType msgType=baseMsg.getMsgType();        switch (msgType){            case LOGIN:{                //向服务器发起登录                LoginMsg loginMsg=new LoginMsg();                loginMsg.setPassword("yao");                loginMsg.setUsername("robin");                channelHandlerContext.writeAndFlush(loginMsg);            }break;            case PING:{                System.out.println("receive ping from server----------");            }break;            case ASK:{                ReplyClientBody replyClientBody=new ReplyClientBody("client info **** !!!");                ReplyMsg replyMsg=new ReplyMsg();                replyMsg.setBody(replyClientBody);                channelHandlerContext.writeAndFlush(replyMsg);            }break;            case REPLY:{                ReplyMsg replyMsg=(ReplyMsg)baseMsg;                ReplyServerBody replyServerBody=(ReplyServerBody)replyMsg.getBody();                System.out.println("receive client msg: "+replyServerBody.getServerInfo());            }            default:break;        }        ReferenceCountUtil.release(msgType);    }protected void channelRead0(ChannelHandlerContext arg0, BaseMsg arg1) throws Exception {}}

服务端处理类,消息类型处理以及对于断开连接的客户端的移除

public class NettyServerHandler extends SimpleChannelInboundHandler<BaseMsg>{ @Override    public void channelInactive(ChannelHandlerContext ctx) throws Exception {        //channel失效,从Map中移除        NettyChannelMap.remove((SocketChannel)ctx.channel());    }    protected void messageReceived(ChannelHandlerContext channelHandlerContext, BaseMsg baseMsg) throws Exception {         if(MsgType.LOGIN.equals(baseMsg.getMsgType())){            LoginMsg loginMsg=(LoginMsg)baseMsg;            if("robin".equals(loginMsg.getUsername())&&"yao".equals(loginMsg.getPassword())){                //登录成功,把channel存到服务端的map中                NettyChannelMap.add(loginMsg.getClientID(),(SocketChannel)channelHandlerContext.channel());                System.out.println("client"+loginMsg.getClientID()+" 登录成功");            }        }else{            if(NettyChannelMap.get(baseMsg.getClientID())==null){                    //说明未登录,或者连接断了,服务器向客户端发起登录请求,让客户端重新登录                    LoginMsg loginMsg=new LoginMsg();                    channelHandlerContext.channel().writeAndFlush(loginMsg);            }        }        switch (baseMsg.getMsgType()){            case PING:{                PingMsg pingMsg=(PingMsg)baseMsg;                PingMsg replyPing=new PingMsg();                NettyChannelMap.get(pingMsg.getClientID()).writeAndFlush(replyPing);            }break;            case ASK:{                //收到客户端的请求                AskMsg askMsg=(AskMsg)baseMsg;                if("authToken".equals(askMsg.getParams().getAuth())){                    ReplyServerBody replyBody=new ReplyServerBody("server info $$$$ !!!");                    ReplyMsg replyMsg=new ReplyMsg();                    replyMsg.setBody(replyBody);                    NettyChannelMap.get(askMsg.getClientID()).writeAndFlush(replyMsg);                }            }break;            case REPLY:{                //收到客户端回复                ReplyMsg replyMsg=(ReplyMsg)baseMsg;                ReplyClientBody clientBody=(ReplyClientBody)replyMsg.getBody();                System.out.println("receive client msg: "+clientBody.getClientInfo());            }break;            default:break;        }        ReferenceCountUtil.release(baseMsg);    }protected void channelRead0(ChannelHandlerContext arg0, BaseMsg arg1) throws Exception {}}

客户端启动类

public class NettyClientBootstrap { private int port;    private String host;    private SocketChannel socketChannel;    private static final EventExecutorGroup group = new DefaultEventExecutorGroup(20);    public NettyClientBootstrap(int port, String host) throws InterruptedException {        this.port = port;        this.host = host;        start();    }    private void start() throws InterruptedException {        EventLoopGroup eventLoopGroup=new NioEventLoopGroup();        Bootstrap bootstrap=new Bootstrap();        bootstrap.channel(NioSocketChannel.class);        bootstrap.option(ChannelOption.SO_KEEPALIVE,true);        bootstrap.group(eventLoopGroup);        bootstrap.remoteAddress(host,port);        bootstrap.handler(new ChannelInitializer<SocketChannel>() {            @Override            protected void initChannel(SocketChannel socketChannel) throws Exception {                socketChannel.pipeline().addLast(new IdleStateHandler(10,5,0));                socketChannel.pipeline().addLast(new ObjectEncoder());                socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));                socketChannel.pipeline().addLast(new NettyClientHandler());            }        });        ChannelFuture future =bootstrap.connect(host,port).sync();        if (future.isSuccess()) {            socketChannel = (SocketChannel)future.channel();            System.out.println("connect server  成功---------");        }    }    public static void main(String[]args) throws InterruptedException {        Constants.setClientID("001");        NettyClientBootstrap bootstrap=new NettyClientBootstrap(9999,"localhost");         LoginMsg loginMsg=new LoginMsg();        loginMsg.setPassword("yao");        loginMsg.setUsername("robin");        bootstrap.socketChannel.writeAndFlush(loginMsg);        while (true){            TimeUnit.SECONDS.sleep(3);            AskMsg askMsg=new AskMsg();            AskParams askParams=new AskParams();            askParams.setAuth("authToken");            askMsg.setParams(askParams);            bootstrap.socketChannel.writeAndFlush(askMsg);        }    }}

服务端启动类

public class NettyServerBootstrap {  private int port;    private SocketChannel socketChannel;    public NettyServerBootstrap(int port) throws InterruptedException {        this.port = port;        bind();    }     private void bind() throws InterruptedException {        EventLoopGroup boss=new NioEventLoopGroup();        EventLoopGroup worker=new NioEventLoopGroup();        ServerBootstrap bootstrap=new ServerBootstrap();        bootstrap.group(boss,worker);        bootstrap.channel(NioServerSocketChannel.class);        bootstrap.option(ChannelOption.SO_BACKLOG, 128);        //通过NoDelay禁用Nagle,使消息立即发出去,不用等待到一定的数据量才发出去        bootstrap.option(ChannelOption.TCP_NODELAY, true);        //保持长连接状态        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {            @Override            protected void initChannel(SocketChannel socketChannel) throws Exception {                ChannelPipeline p = socketChannel.pipeline();                p.addLast(new ObjectEncoder());                p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));                p.addLast(new NettyServerHandler());            }        });        ChannelFuture f= bootstrap.bind(port).sync();        if(f.isSuccess()){            System.out.println("server start---------------");        }    }    public static void main(String []args) throws InterruptedException {        NettyServerBootstrap bootstrap=new NettyServerBootstrap(9999);        while (true){            SocketChannel channel=(SocketChannel)NettyChannelMap.get("001");            if(channel!=null){                AskMsg askMsg=new AskMsg();                channel.writeAndFlush(askMsg);            }            TimeUnit.SECONDS.sleep(5);        }    }}