赞
踩
服务端代码
NettyServer
- package com.example.netty.netty2;
-
- import io.netty.bootstrap.ServerBootstrap;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelOption;
- import io.netty.channel.EventLoopGroup;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.nio.NioServerSocketChannel;
- import lombok.extern.slf4j.Slf4j;
-
- @Slf4j
- public class NettyServer {
-
- private NettyServerChannelInitializer serverChannelInitializer = null;
-
- private int port = 8888;
-
-
- public void bind() throws Exception {
- //配置服务端的NIO线程组
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
- serverChannelInitializer = new NettyServerChannelInitializer();
- ServerBootstrap b = new ServerBootstrap();
- b.group(bossGroup, workerGroup)
- .channel(NioServerSocketChannel.class)
- //保持长连接
- .childOption(ChannelOption.SO_KEEPALIVE,true)
- .option(ChannelOption.SO_BACKLOG, 1024)
- .childHandler(serverChannelInitializer);
-
- //绑定端口,同步等待成功
- ChannelFuture f = b.bind(port).sync();
-
-
- //等待服务器监听端口关闭
- f.channel().closeFuture().sync();
- } finally {
- //释放线程池资源
- bossGroup.shutdownGracefully();
- workerGroup.shutdownGracefully();
- }
- }
-
- public static void main(String[] args) throws Exception {
- new NettyServer().bind();
- }
-
- }
自定义channel类 里面涉及有拆包,粘包,编解码以及其他自定义操作(心跳检测)
- package com.example.netty.netty2;
-
- import io.netty.buffer.Unpooled;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.ChannelPipeline;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.handler.codec.DelimiterBasedFrameDecoder;
- import io.netty.handler.codec.string.StringDecoder;
- import io.netty.handler.codec.string.StringEncoder;
- import io.netty.handler.timeout.IdleStateHandler;
- import java.util.concurrent.TimeUnit;
-
-
- public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
-
- private NettyServerHandler handler ;
-
-
-
- @Override
- protected void initChannel(SocketChannel socketChannel) throws Exception {
- ChannelPipeline pipeline = socketChannel.pipeline();
- //解决TCP粘包拆包的问题,以特定的字符结尾($_)
- pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer("$_".getBytes())));
- //字符串解码和编码
- pipeline.addLast("decoder", new StringDecoder());
- pipeline.addLast("encoder", new StringEncoder());
- pipeline.addLast(new IdleStateHandler(3,0,0,TimeUnit.SECONDS));
- //服务器的逻辑
- handler = new NettyServerHandler();
- pipeline.addLast("handler", handler);
- }
- }
NettyServerHandler 心跳检测
- package com.example.netty.netty2;
-
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.SimpleChannelInboundHandler;
- import io.netty.handler.timeout.IdleState;
- import io.netty.handler.timeout.IdleStateEvent;
- import lombok.extern.slf4j.Slf4j;
-
-
- @Slf4j
- public class NettyServerHandler extends SimpleChannelInboundHandler {
-
- /**
- * 心跳丢失次数
- */
- private int counter = 0;
-
-
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
- System.out.println("Client say : " + msg.toString());
- //重置心跳丢失次数
- counter = 0;
- }
-
-
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- log.info("服务端被 : " + ctx.channel().remoteAddress().toString()+ " 连接上了 !");
- super.channelActive(ctx);
- }
-
-
- @Override
- public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
- if (evt instanceof IdleStateEvent) {
- IdleStateEvent event = (IdleStateEvent) evt;
- if (event.state().equals(IdleState.READER_IDLE)){
- // 空闲40s之后触发 (心跳包丢失)
- if (counter >= 3) {
- // 连续丢失3个心跳包 (断开连接)
- ctx.channel().close().sync();
- log.error("已与"+ctx.channel().remoteAddress()+"断开连接");
- System.out.println("已与"+ctx.channel().remoteAddress()+"断开连接");
- } else {
- counter++;
- log.debug(ctx.channel().remoteAddress() + "丢失了第 "+ctx.channel().remoteAddress() + counter + " 个心跳包");
- System.out.println("丢失了"+ctx.channel().remoteAddress()+"第 " + counter + " 个心跳包");
- }
- }
-
- }
- }
- }
客户端
NettyClient
- package com.example.netty.netty2;
-
- import io.netty.bootstrap.Bootstrap;
- import io.netty.channel.Channel;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelFutureListener;
- import io.netty.channel.ChannelOption;
- import io.netty.channel.EventLoop;
- import io.netty.channel.EventLoopGroup;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.nio.NioSocketChannel;
- import java.util.concurrent.TimeUnit;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Value;
-
- @Slf4j
- public class NettyClient {
-
- private String host;
-
- private int port;
-
- private static Channel channel;
-
- public NettyClient(){
-
- }
-
- public NettyClient(String host, int port) {
- this.host = host;
- this.port = port;
- }
-
-
-
- public void start() {
- EventLoopGroup group = new NioEventLoopGroup();
- try {
- Bootstrap b = new Bootstrap();
- b.group(group)
- .option(ChannelOption.SO_KEEPALIVE,true)
- .channel(NioSocketChannel.class)
- .handler(new ClientChannelInitializer(host,port));
-
- ChannelFuture f = b.connect(host,port);
-
- // //断线重连
- f.addListener(new ChannelFutureListener() {
- @Override
- public void operationComplete(ChannelFuture channelFuture) throws Exception {
- if (!channelFuture.isSuccess()) {
- final EventLoop loop = channelFuture.channel().eventLoop();
- loop.schedule(new Runnable() {
- @Override
- public void run() {
- log.error("服务端链接不上,开始重连操作...");
- System.err.println("服务端链接不上,开始重连操作...");
- start();
- }
- }, 1L, TimeUnit.SECONDS);
- } else {
- channel = channelFuture.channel();
- log.info("服务端链接成功...");
- System.err.println("服务端链接成功...");
- }
- }
- });
-
- }catch (Exception e){
- e.printStackTrace();
- }
- }
-
- public static void main(String[] args) {
- new NettyClient ("127.0.0.1",8888).start();
- }
- }
ClientChannelInitializer
- package com.example.netty.netty2;
-
- import io.netty.buffer.Unpooled;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.ChannelPipeline;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.handler.codec.DelimiterBasedFrameDecoder;
- import io.netty.handler.codec.string.StringDecoder;
- import io.netty.handler.codec.string.StringEncoder;
- import io.netty.handler.timeout.IdleStateHandler;
- import java.util.concurrent.TimeUnit;
-
- public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
-
- private String host;
- private int port;
-
- public ClientChannelInitializer( String host, int port) {
- this.host = host;
- this.port = port;
- }
-
- @Override
- protected void initChannel(SocketChannel socketChannel) throws Exception {
- ChannelPipeline pipeline = socketChannel.pipeline();
- //解决TCP粘包拆包的问题,以特定的字符结尾($_)
- pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer("$_".getBytes())));
- //字符串解码和编码
- pipeline.addLast("decoder", new StringDecoder());
- pipeline.addLast("encoder", new StringEncoder());
- //心跳检测
- pipeline.addLast(new IdleStateHandler(0,10,0,TimeUnit.SECONDS));
- //客户端的逻辑
- pipeline.addLast("handler", new NettyClientHandler(host,port));
-
- }
- }
NettyClientHandler
- package com.example.netty.netty2;
-
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.EventLoop;
- import io.netty.channel.SimpleChannelInboundHandler;
- import io.netty.handler.timeout.IdleState;
- import io.netty.handler.timeout.IdleStateEvent;
- import java.util.concurrent.TimeUnit;
- import lombok.extern.slf4j.Slf4j;
-
- @Slf4j
- public class NettyClientHandler extends SimpleChannelInboundHandler {
-
- private String host;
- private int port;
- private NettyClient nettyClinet;
- private String tenantId;
-
-
- public NettyClientHandler(String host, int port) {
- this.host = host;
- this.port = port;
- nettyClinet = new NettyClient(host,port);
- }
-
- @Override
- protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o)
- throws Exception {
- System.out.println("Server say : " + o.toString());
- }
-
-
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- System.out.println("通道已连接!!");
- }
-
- @Override
- public void channelInactive(ChannelHandlerContext ctx) throws Exception {
- System.out.println("断线了。。。。。。");
- //使用过程中断线重连
- final EventLoop eventLoop = ctx.channel().eventLoop();
- eventLoop.schedule(new Runnable() {
- @Override
- public void run() {
- nettyClinet.start();
- }
- }, 1, TimeUnit.SECONDS);
-
- ctx.fireChannelInactive();
- }
-
- @Override
- public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
- throws Exception {
- if (evt instanceof IdleStateEvent) {
- IdleStateEvent event = (IdleStateEvent) evt;
- if (event.state().equals(IdleState.READER_IDLE)) {
- System.out.println("READER_IDLE");
-
- } else if (event.state().equals(IdleState.WRITER_IDLE)) {
- /**发送心跳,保持长连接*/
- String s = "ping$_";
- ctx.channel().writeAndFlush(s);
- log.debug("心跳发送成功!");
- System.out.println("心跳发送成功!");
- } else if (event.state().equals(IdleState.ALL_IDLE)) {
- System.out.println("ALL_IDLE");
- }
- }
- super.userEventTriggered(ctx, evt);
- }
-
- }
心跳检测类里面关键代码IdleStateHandler
readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息) writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息) allIdleTime:所有类型的超时时间 比如在这里,服务端每隔3S检测一下,若超过3次,就自动断开连接, 客户端是每个10S中发送一个心跳包到服务端。 so,如非异常情况,会一直保持通信,因为服务端在将要端口连接的时候,这个时候客户端发送了一个心跳包。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。