服务端:
public class NettySer {
public static void main(String[] args) {
NettySer ns = new NettySer();
ns.start();
}
private void start() {
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup();// 默认为cpu数乘2
try {
ServerBootstrap sb = new ServerBootstrap();
// 设置服务端ServerBootStrap启动参数
sb.group(boss, worker);
sb.channel(NioServerSocketChannel.class); // 我要指定使用NioServerSocketChannel这种类型的通道
sb.option(ChannelOption.SO_BACKLOG, 1024);// 设置tcp缓冲区, 支持多少个客户端连接
sb.childHandler(new ChannelInitializer<Channel>() {// 一定要使用 childHandler 去绑定具体的事件处理器
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(1024 * 1024 * 1024,
ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
// key是ip, value是认证码
private HashMap<String, String> AUTH_IP_MAP = new HashMap<>();
// 成功链接标志
private final String SUCCESS_KEY = "auth_success_key";
// 模拟认证码(一个客户端一个), 实际开发中存在数据库里
{
AUTH_IP_MAP.put("10.10.10.22", "1234");
}
/**
* 安全认证
*/
private boolean auth(ChannelHandlerContext ctx, Object msg) {
String[] ret = ((String) msg).split(",");
String auth = AUTH_IP_MAP.get(ret[0]);
if (auth != null && auth.equals(ret[1])) {
ctx.writeAndFlush(SUCCESS_KEY);
return true;
} else {
ctx.writeAndFlush("auth failure !").addListener(ChannelFutureListener.CLOSE);
return false;
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof String) { // 第一次连接要进行安全认证
System.out.println("收到 string msg=" + msg);
auth(ctx, msg);
} else if (msg instanceof RequestInfo) {// 心跳包
System.out.println("收到 RequestInfo msg=" + msg);
RequestInfo info = (RequestInfo) msg;
System.out.println("--------------------------------------------");
System.out.println("当前主机ip为: " + info.getIp());
System.out.println("当前主机cpu情况: ");
HashMap<String, Object> cpu = info.getCpuPercMap();
System.out.println("总使用率: " + cpu.get("combined"));
System.out.println("当前主机memory情况: ");
HashMap<String, Object> memory = info.getMemoryMap();
System.out.println("内存总量: " + memory.get("total"));
System.out.println("--------------------------------------------");
ctx.writeAndFlush("info received!");
} else {
ctx.writeAndFlush("connect failure!").addListener(ChannelFutureListener.CLOSE);
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client active");
super.channelActive(ctx);
}
});
}
});
// 绑定指定的端口进行监听
ChannelFuture f = sb.bind(9090).sync();
// 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
客户端:
public class NettyCli {
public static void main(String[] args) {
NioEventLoopGroup worker = new NioEventLoopGroup();// 默认为cpu数乘2
try {
Bootstrap b = new Bootstrap();
b.group(worker);
b.channel(NioSocketChannel.class);
b.handler(new ChannelInitializer() {
// @Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(1024 * 1024 * 1024,
ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
// 定时任务
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture<?> heartBeat;
// 本机ip地址
private InetAddress addr;
private static final String SUCCESS_KEY = "auth_success_key";
// 密码
private String key = "1234";
/**
* 第一次连接要进行安全认证, 发送认证信息要服务器 此方法在第一次和服务端链接时调用,服务端会在read方法里收到msg
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("ser active");
addr = InetAddress.getLocalHost();
String ip = addr.getHostAddress();
// 认证信息
String auth = ip + "," + key;
ctx.writeAndFlush(auth);
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
if (msg instanceof String) { // 收到服务端消息,已建立连接
String ret = (String) msg;
System.out.println("cli channelRead msg = " + msg);
if (SUCCESS_KEY.equals(ret)) {// 认证成功,开始发心跳
// 定时发送心跳消息
System.out.println("定时发送心跳消息..");
this.heartBeat = this.scheduler.scheduleWithFixedDelay(new HeartBeatTask(ctx),
0, 2, TimeUnit.SECONDS);
} else if ("info received!".equals(ret)){// 收到服务端心跳回复
System.out.println("收到心跳");
}else {//认证失败
System.out.println("收到心跳");
}
}
} finally {
ReferenceCountUtil.release(msg);
}
}
/**
* 心跳信息类
*/
class HeartBeatTask implements Runnable {
private final ChannelHandlerContext ctx;
public HeartBeatTask(final ChannelHandlerContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
try {
RequestInfo info = new RequestInfo();
/*
* 组装 info
*/
// ip
info.setIp(addr.getHostAddress());
// cpu prec
HashMap<String, Object> cpuPercMap = new HashMap<>();
cpuPercMap.put("combined", "jww");
// memory
HashMap<String, Object> memoryMap = new HashMap<>();
memoryMap.put("total", "jww");
info.setCpuPercMap(cpuPercMap);
info.setMemoryMap(memoryMap);
ctx.writeAndFlush(info);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
});
// 连接Server
final ChannelFuture future = b.connect("127.0.0.1", 9090).sync();
// 等待连接关闭
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
worker.shutdownGracefully();
}
}
}
心跳类:
public class RequestInfo implements Serializable {
private static final long serialVersionUID = 1L;
private String ip ;
private HashMap<String, Object> cpuPercMap ; //cpu状态
private HashMap<String, Object> memoryMap; //内存状态
//... other field
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public HashMap<String, Object> getCpuPercMap() {
return cpuPercMap;
}
public void setCpuPercMap(HashMap<String, Object> cpuPercMap) {
this.cpuPercMap = cpuPercMap;
}
public HashMap<String, Object> getMemoryMap() {
return memoryMap;
}
public void setMemoryMap(HashMap<String, Object> memoryMap) {
this.memoryMap = memoryMap;
}
}