使用Netty实现自定义推送

在推送中主要是为了维持tcp长链接,其中采用的技术包括ping/pong 断开从连。本文简单的实现:

重要数据结构:

public enum MsgType{

  PING,PONG,LONGIN,PUSH

}

//所有消息类型的基类

public class BaseMsg implements Serializable{

private static final long serialVersionUID = 1L;
private String clientId;//唯一标示防止channel调用混乱
private MsgType type;

public BaseMsg(){
this.clientId = Constants.getClientId();
}


public String getClientId() {
return clientId;
}


public void setClientId(String clientId) {
this.clientId = clientId;
}


public MsgType getType() {
return type;
}


public void setType(MsgType type) {
this.type = type;
}

}


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{
private String userName;
private String password;

public LoginMsg(){
setType(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 class PingMsg extends BaseMsg{
  public PingMsg(){
 super();
 setType(MsgType.PING);
  }
}


public class PongMsg extends BaseMsg{
   public PongMsg(){
  setType(MsgType.PONG);
   }
}


public class PushMsg extends BaseMsg {
private String title;
private String content;


public PushMsg() {
setType(MsgType.PUSH);
}


public String getTitle() {
return title;
}


public void setTitle(String title) {
this.title = title;
}


public String getContent() {
return content;
}


public void setContent(String content) {
this.content = content;
}


}


保存Channel的类

public  class NettyChannelMap {
   private Map<String, SocketChannel> map = new ConcurrentHashMap<String, SocketChannel>();
   private static NettyChannelMap instance = new NettyChannelMap();
   
   private NettyChannelMap(){
  
   }
   
   public static NettyChannelMap getInstance(){
  return instance;
   }
   
   public void addSocketChannel(String clientId,SocketChannel socketChannel){
  map.put(clientId, socketChannel);
   }
   
   public SocketChannel getSocketChannel(String clientId){
  return map.get(clientId);
   }
   
   public void removeSocketChannel(String clientId){
  map.remove(clientId);
   }
   
   public void removeSocketChannel(SocketChannel socketChannel){
  if(map.containsValue(socketChannel)){//查看是否包含
  String key = null;
  SocketChannel value = null;
for(Map.Entry<String, SocketChannel> entry: map.entrySet()){
key = entry.getKey();
value = entry.getValue();
if(value==socketChannel){
break;
}
}
map.remove(key);
  }
   }
   
   
   public void pushMsg2AllClient(PushMsg pushMsg){
  if(map.size()==0)
  return;
for(SocketChannel socketChannel : map.values()){
socketChannel.writeAndFlush(pushMsg);
}
   }
}


服务端处理的Handler

public class NettyServerHandler extends SimpleChannelInboundHandler<BaseMsg>{


@Override
protected void messageReceived(ChannelHandlerContext ctx, BaseMsg baseMsg) throws Exception {
SocketChannel socketChannel = (SocketChannel) ctx.channel();
// 这里为客户端传过来的信息
switch (baseMsg.getType()) {
case PING:
PingMsg pingMsg = (PingMsg) baseMsg;
PongMsg pongMsg = new PongMsg();
SocketChannel socketChannel2 = NettyChannelMap.getInstance().getSocketChannel(pingMsg.getClientId());
socketChannel2.writeAndFlush(pongMsg);
System.out.println("收到PING类消息,clientId为"+pingMsg.getClientId());
break;
case LOGIN:
LoginMsg loginMsg = (LoginMsg) baseMsg;
   NettyChannelMap.getInstance().addSocketChannel(loginMsg.getClientId(), socketChannel);
   System.out.println("CientId:"+loginMsg.getClientId()+" userName:"+loginMsg.getUserName()+" password:"+loginMsg.getPassword()+"登录成功!");
break;
default:
System.out.println("------defautl-------");
break;
}
}

@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// channel失效,从Map中移除
SocketChannel socketChannel = (SocketChannel) ctx.channel();
NettyChannelMap.getInstance().removeSocketChannel(socketChannel);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
System.out.println("出现异常:"+cause.getMessage());
ctx.close();
}


}



客户端的处理类:

public class NettyClientHandler extends SimpleChannelInboundHandler<BaseMsg>{
//设置心跳时间 开始时间
public static final int MIN_CLICK_DELAY_TIME = 1000*30;
private int tryTime = 0;
public static final int MAX_TRY_TIME = 8;

private static Map<String, SocketChannel> Map = new ConcurrentHashMap<String, SocketChannel>();


@Override
protected void messageReceived(ChannelHandlerContext ctx, BaseMsg baseMsg) throws Exception {
// TODO Auto-generated method stub
switch (baseMsg.getType()) {
case LOGIN:

break;


case PONG:
System.out.println("接受到服务器端的PONG");
break;

case PUSH:
PushMsg pushMsg =(PushMsg) baseMsg;
System.out.println("收到服务器端的推送消息:主题"+pushMsg.getTitle()+" 内容:"+pushMsg.getContent());
break;

default:
System.out.println("-------default--------");
break;
}
}

@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
tryTime=0;
LoginMsg loginMsg = new LoginMsg();
loginMsg.setPassword("yao"+System.currentTimeMillis());
loginMsg.setUserName("robot"+System.currentTimeMillis());
final String clientId= System.currentTimeMillis()+"";
loginMsg.setClientId(clientId);
Map.put(clientId, (SocketChannel) ctx.channel());
//开启线程进行ping
new Thread(new Runnable() {//每隔180秒发送一个ping

public void run() {
while(true){
try {
Thread.sleep(1000*180);
PingMsg pingMsg = new PingMsg();
pingMsg.setClientId(clientId);
ctx.writeAndFlush(pingMsg);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
ctx.writeAndFlush(loginMsg);
}

@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 断线后的操作
super.channelInactive(ctx);
tryTime++;
if(tryTime>MAX_TRY_TIME)
return;
System.out.println("从连接中。。。。。。。");
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
System.out.println("出现异常啦:"+cause.getMessage());
ctx.close();
}


}


启动类:

public class NettyClientBootstrap {
    public static void main(String[] args) throws InterruptedException {
  EventLoopGroup work = new NioEventLoopGroup();
  Bootstrap bootstrap = new Bootstrap();
  bootstrap.group(work).channel(NioSocketChannel.class)
  .option(ChannelOption.SO_KEEPALIVE, true)
  .handler(new ChannelInitializer<SocketChannel>() {


@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// TODO Auto-generated method stub
socketChannel.pipeline().addLast(new ObjectEncoder());
socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
socketChannel.pipeline().addLast(new NettyClientHandler());
}
});
  
  ChannelFuture future = bootstrap.connect("127.0.0.1",9999).sync();
  System.out.println("推送客户端。。。。。。启动");
 
  
  future.channel().closeFuture().sync();
  work.shutdownGracefully();
  
  
}
}



public class NettyServerBootstrap {
    public static void main(String[] args) throws InterruptedException {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss,work).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.option(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {


@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ObjectEncoder());
socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
socketChannel.pipeline().addLast(new NettyServerHandler());
}
});

ChannelFuture future = bootstrap.bind(9999).sync();
System.out.println("推送服务端启动...........");


//开启定时器推送
new Thread(new Runnable() {

public void run() {
try {
while (true) {
Thread.sleep(1000*60*5);
PushMsg pushMsg = new PushMsg();
pushMsg.setContent("当前时间为:"+new Date());
pushMsg.setTitle("整点报时");
NettyChannelMap.getInstance().pushMsg2AllClient(pushMsg);
}

} catch (InterruptedException e) {
e.printStackTrace();
}

}
}).start();
future.channel().closeFuture().sync();
boss.shutdownGracefully();
work.shutdownGracefully();
}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值