基本配置和上文一样,不在赘述了,直接贴代码
1.nettClient
/**
* @author Simon Hua
* @date 2020/2/11 9:45
*/
@Component
public class NettyClient {
@Value("${netty.server.host}")
private String netty_server_host;
@Value("${netty.server.port}")
private int netty_server_port;
@Autowired
private CustomChannelInitializer customChannelInitializer;
private static Logger logger = LoggerFactory.getLogger(NettyClient.class);
/// 通过nio方式来接收连接和处理连接
private EventLoopGroup group = new NioEventLoopGroup();
private static Bootstrap bootstrap = new Bootstrap();
public static Channel channel;
/**
* Netty创建全部都是实现自AbstractBootstrap。
* 客户端的是Bootstrap,服务端的则是 ServerBootstrap。
* @throws InterruptedException
* @throws IOException
**/
@PostConstruct
public void initChannel() {
try {
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(customChannelInitializer);
bootstrap.remoteAddress(netty_server_host,netty_server_port);
// 连接服务端
ChannelFuture f = bootstrap.connect();
f.addListener(new ConnectionListener());
channel = f.channel();
// channel = bootstrap.connect().sync().channel();
} catch (NumberFormatException e) {
JSONObject json=new JSONObject();
json.put("code",-1);
json.put("msg","启动netty客户端异常,请检查netty监听端口是否正确!");
logger.error(json.toJSONString(),e);
}
}
/**
* 重连
*/
public static void doConnect() {
ChannelFuture f = null;
try {
if (bootstrap != null) {
f = bootstrap.connect().addListener(new ConnectionListener());
channel = f.channel();
}
} catch (Exception e) {
JSONObject json=new JSONObject();
json.put("code",-1);
json.put("msg","客户端连接失败!");
logger.error(json.toJSONString(),e);
}
}
}
2.CustomChannelInitializer
/**
* @author Simon Hua
* @date 2020/2/11 9:48
*
* 自定义ChannelInitializer,用于初始化ChannelPipeline
*/
@Component
public class CustomChannelInitializer extends ChannelInitializer {
@Resource
private NettyClientHandler nettyClientHandler;
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline channelPipeline = channel.pipeline();
// channelPipeline.addLast("framer", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2));
// channelPipeline.addLast("prepender", new LengthFieldPrepender(4, false));
//心跳发送时间间隔
channelPipeline.addLast(new IdleStateHandler(0, 60, 0, TimeUnit.SECONDS));
// 解码和编码,应和客户端一致
//传输协议 Protobuf
channelPipeline.addLast(new ProtobufVarint32FrameDecoder());
channelPipeline.addLast(new ProtobufDecoder(NettyMessage.Content.getDefaultInstance()));
channelPipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
channelPipeline.addLast(new ProtobufEncoder());
//客户端的逻辑
channelPipeline.addLast("handler",nettyClientHandler);
}
}
3.handler
/**
* @author Simon Hua
* @date 2020/2/11 9:52
*
* netty消息处理
*/
@Component
@ChannelHandler.Sharable
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(NettyClientHandler.class);
/**
* 建立连接时
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("客户端与服务器建立连接,当前时间:{}", Instant.now().toString());
ctx.fireChannelActive();
}
/**
* 关闭连接时
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.error("客户端失去服务端连接,时间:{}",Instant.now().toString());
//使用过程中断线重连
final EventLoop eventLoop = ctx.channel().eventLoop();
eventLoop.schedule(new Runnable() {
@Override
public void run() {
logger.info("客户端尝试重新连接服务器");
NettyClient.doConnect();
}
}, 1L, TimeUnit.SECONDS);
super.channelInactive(ctx);
}
/**
* 心跳请求处理
* 每60秒发送一次心跳请求;
*
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception {
logger.info("\nsend heartbeat to server");
if (obj instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) obj;
if (IdleState.WRITER_IDLE.equals(event.state())) { //写通道处于空闲状态,就发送心跳命令
NettyMessage.Content heartBeat = NettyMessage.Content.newBuilder().
setOperateType(NettyConstant.HEART_BEAT).
build();
ctx.writeAndFlush(heartBeat);
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.error("客户端异常,异常信息:{}", cause.getMessage());
cause.printStackTrace();
ctx.close();
}
//业务逻辑处理
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//非protobuf格式数据
if (!(msg instanceof NettyMessage.Content)) {
logger.error("invalid message");
return;
}
try {
NettyMessage.Content message = (NettyMessage.Content) msg;
String operateType = message.getOperateType();
NettyMessage.Content.Builder responseBuilder = NettyMessage.Content.newBuilder();
}catch (Exception e) {
JSONObject json=new JSONObject();
json.put("code",-1);
logger.error(json.toJSONString(),e);
} finally {
ReferenceCountUtil.release(msg);
}
}
}
4.listener
/**
* @author Simon Hua
* @date 2020/2/11 9:47
*/
public class ConnectionListener implements ChannelFutureListener {
private static Logger logger = LoggerFactory.getLogger(ConnectionListener.class);
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (!channelFuture.isSuccess()) {
final EventLoop loop = channelFuture.channel().eventLoop();
loop.schedule(()->{
logger.error("客户端连接服务器失败,开始重连操作...");
NettyClient.doConnect();
}, 5L, TimeUnit.SECONDS);
JSONObject json=new JSONObject();
json.put("code",-1);
json.put("msg","客户端连接服务器失败,开始重连操作...");
logger.error(json.toJSONString());
} else {
JSONObject json=new JSONObject();
json.put("code",0);
json.put("msg","客户端连接服务器成功!");
logger.error(json.toJSONString());
}
}
}