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

本文详细介绍了如何使用Netty框架实现消息推送功能,并探讨了内部的心跳机制。包括通讯信息基类的设计,实现了序列化和信息类型、客户端ID的管理;客户端登录信息的处理,以及消息协议类型的枚举定义;通过Channel管理客户端,实现添加、移除和获取操作;心跳包数据类型定义,消息回应类和序列化信息主体的实现;客户端和服务端的消息处理逻辑,特别是心跳处理机制,以及客户端和服务端的启动流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);

	        }

	    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值