1、NettyServer
2、NettyServerChannelInitializer
3、NettyServerHandler
package web
import com.hbscTools.NettyServer
import grails.boot.GrailsApp
import grails.boot.config.GrailsAutoConfiguration
class Application extends GrailsAutoConfiguration {
static void main(String[] args) {
GrailsApp.run(Application, args)
//初始化netty
NettyServer nettyServer = new NettyServer();
nettyServer.bind();
}
}
//服务器推送消息至指定客户端
NettyServerHandler.sendMsg2Client(机器编号, 消息体)
.addListener({ future ->//异步监听发送结果
})
//服务器群发消息至所有在线客户端
//还没写,等有需要再写!
...
...
...
@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之后触发 (心跳包丢失)
Integer counter = online_channels_heart.get(ctx.channel().id().asLongText());
if (counter >= 3) {
// 连续丢失3个心跳包 (断开连接)
ctx.channel().close().sync();
System.out.println("已与" + ctx.channel().remoteAddress() + "断开连接");
} else {
counter++;
//重置心跳丢失次数
online_channels_heart.replace(ctx.channel().id().asLongText(), counter);
System.out.println("丢失了第 " + counter + " 个心跳包");
}
}
}
}
ChannelPipeline pipeline = ch.pipeline();
//解决TCP粘包拆包的问题,以特定的字符结尾($_)
pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer("$_".getBytes())));
package com.hbscTools;
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;
/**
* author : ZZL(周子林)
* e-mail : [email protected]
* date : 2019/12/17 16:37
* desc :
* version: 1.0
*/
public class NettyServer {
private int port = 8888;
private NettyServerChannelInitializer serverChannelInitializer = null;
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();
}
}
}
package com.hbscTools;
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;
/**
* author : ZZL(周子林)
* e-mail : [email protected]
* date : 2019/12/17 16:37
* desc :
* version: 1.0
*/
public class NettyServerChannelInitializer extends ChannelInitializer {
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(40,0,0, TimeUnit.SECONDS));
//服务器的逻辑
handler = new NettyServerHandler();
pipeline.addLast("handler", handler);
}
}
package com.hbscTools;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
/**
* author : ZZL(周子林)
* e-mail : [email protected]
* date : 2019/12/17 16:37
* desc :
* version: 1.0
*/
public class NettyServerHandler extends SimpleChannelInboundHandler {
//新建一个channelGroup,用于存放连接的channel
public static HashMap online_channels = new HashMap();
//记录每一个channel的心跳包丢失次数
public HashMap online_channels_heart = new HashMap();
public static ChannelFuture sendMsg2Client(String id, String msg) {
ChannelHandlerContext ctx = NettyServerHandler.online_channels.get(id);
if (null == ctx) {
System.out.println("Channel is not exist");
return null;
}
//返回ChannelFuture ,异步回调结果,若消息发送失败,则继续发送
//若要保证数据的完整性,则需要做MD5数据校验
ChannelFuture future = ctx.channel()
.writeAndFlush(msg.concat("$_"));
return future;
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("RemoteAddress : " + ctx.channel().remoteAddress().toString() + " add !");
online_channels.put(ctx.channel().id().asLongText(), ctx);
online_channels_heart.put(ctx.channel().id().asLongText(), 0);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("RemoteAddress : " + ctx.channel().remoteAddress().toString() + " remove !");
online_channels.remove(ctx.channel().id().asLongText());
online_channels_heart.remove(ctx.channel().id().asLongText());
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
System.out.println("Client: " + channelHandlerContext.channel().id().asLongText() + " say : " + o.toString());
System.out.println(new Date());
//add channel(重连)
online_channels.putIfAbsent(channelHandlerContext.channel().id().asLongText(), channelHandlerContext);
//重置心跳丢失次数
online_channels_heart.replace(channelHandlerContext.channel().id().asLongText(), 0);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("RemoteAddress : " + ctx.channel().remoteAddress().toString() + " active !");
}
@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之后触发 (心跳包丢失)
Integer counter = online_channels_heart.get(ctx.channel().id().asLongText());
if (counter >= 3) {
// 连续丢失3个心跳包 (断开连接)
ctx.channel().close().sync();
System.out.println("已与" + ctx.channel().remoteAddress() + "断开连接");
} else {
counter++;
//重置心跳丢失次数
online_channels_heart.replace(ctx.channel().id().asLongText(), counter);
System.out.println("丢失了第 " + counter + " 个心跳包");
}
}
}
}
}
1、NettyClient
2、NettyClientChannelInitializer
3、NettyClientHandler
在app包下面的build.gradle中加入
dependencies {
...
...
implementation group: 'io.netty', name: 'netty-all', version: '4.1.16.Final'
}
在MyApplication的onCreate()
@Override
public void onCreate() {
super.onCreate();
/* 初始化Netty */
new NettyClient("192.168.13.58", 8888).connect();
}
消息读取
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Server say : " + msg.toString());
//在此获取服务端消息,处理业务逻辑
//EventBus事件传递,牵涉到EventBus的使用,建议先了解一下EventBus相关的原理以及用法
EventBus.getDefault().post(msg)
}
@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);
System.out.println("心跳发送成功!");
} else if (event.state().equals(IdleState.ALL_IDLE)) {
System.out.println("ALL_IDLE");
}
}
super.userEventTriggered(ctx, evt);
}
package com.hbsc.automachine000.netty;
import java.util.concurrent.TimeUnit;
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;
/**
* author : ZZL(周子林)
* e-mail : [email protected]
* date : 2019/12/17 16:37
* desc :
* version: 1.0
*/
public class NettyClient {
private String host;
private Integer port;
private static Channel channel;
public NettyClient(String host, Integer port) {
this.host = host;
this.port = port;
}
public void connect() {
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(() -> {
System.err.println("服务端链接不上,开始重连操作...");
connect();
}, 1L, TimeUnit.SECONDS);
} else {
channel = channelFuture.channel();
System.err.println("服务端链接成功...");
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.hbsc.automachine000.netty;
import java.util.concurrent.TimeUnit;
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;
/**
* author : ZZL(周子林)
* e-mail : [email protected]
* date : 2019/12/17 16:40
* desc :
* version: 1.0
*/
public class ClientChannelInitializer extends ChannelInitializer {
private String host;
private Integer port;
public ClientChannelInitializer(String host, Integer port) {
this.host = host;
this.port = port;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.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, 30, 0, TimeUnit.SECONDS));
//客户端的逻辑
pipeline.addLast("handler", new NettyClientHandler(host, port));
}
}
package com.hbsc.automachine000.netty;
import java.util.concurrent.TimeUnit;
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;
/**
* author : ZZL(周子林)
* e-mail : [email protected]
* date : 2019/12/17 16:43
* desc :
* version: 1.0
*/
public class NettyClientHandler extends SimpleChannelInboundHandler {
private String host;
private Integer port;
private NettyClient nettyClient;
public NettyClientHandler(String host, Integer port) {
this.host = host;
this.port = port;
nettyClient = new NettyClient(host,port);
}
//这个方法接收不到数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { }
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Server say : " + msg.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(() -> {
nettyClient.connect();
}, 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);
System.out.println("心跳发送成功!");
} else if (event.state().equals(IdleState.ALL_IDLE)) {
System.out.println("ALL_IDLE");
}
}
super.userEventTriggered(ctx, evt);
}
}