springboot Netty搭建与使用 Java与机器通信
Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
我Netty主要是与机器进行通讯 一台服务对多台机器
作为一个开发者,我以业务实现为主 上代码
NettyServer .java
package com.springboot.exam.controller.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* date: 2019-07-11 11:11
**/
@Component
public class NettyServer {
private static final Logger log = LoggerFactory.getLogger(NettyServer.class);
//boss事件轮询线程组
private EventLoopGroup boss = new NioEventLoopGroup();
//worker事件轮询线程组
private EventLoopGroup worker = new NioEventLoopGroup();
private Channel channel;
//连接map
public static Map<String, ChannelHandlerContext> map = new HashMap<String, ChannelHandlerContext>();
@Value("${n.port}")
private Integer port;
@Value("${n.url}")
private String url;
/**
* 开启Netty服务
*
* @return
*/
public ChannelFuture start() throws InterruptedException {
// 1. 创建一个线程组:接收客户端连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 2. 创建一个线程组:处理网络操作
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 3. 创建服务器端启动助手来配置参数
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)//设置两个线程组
.channel(NioServerSocketChannel.class)//5.使用NioServerSocketChannel作为服务器端通道的实现
.option(ChannelOption.SO_BACKLOG,1024)//6.设置线程队列中等待连接的个数
.childOption(ChannelOption.SO_KEEPALIVE,true)//7.保持活动连接状态
.childHandler(new ChannelInitializer<SocketChannel>() {//8.创建一个通道初始化对象
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {//9.往pipeline链添加自定义的handler类
//socketChannel.pipeline().addLast(new LineBasedFrameDecoder(10010));
//字符串解码和编码
//LineBasedFrameDecoder + StringDecoder 就是一个按行切换的文本解码器。
//socketChannel.pipeline().addLast( new StringDecoder());
//socketChannel.pipeline().addLast( new StringEncoder());
socketChannel.pipeline().addLast(new NettyServerHandler());
}
});
ChannelFuture cf = b.bind(port).sync();//绑定端口 非阻塞
//ChannelFuture cf = b.bind(url, port);
ChannelFuture channelFuture1 = cf.syncUninterruptibly();//接收连接
channel = channelFuture1.channel();//获取通道
if (channelFuture1 != null && channelFuture1.isSuccess()) {
log.info("Netty server 服务启动成功,端口port = {}", port);
} else {
log.info("Netty server start fail");
}
cf.channel().closeFuture().sync();//异步
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
return cf;
}
/**
* 停止Netty服务
*/
public void destroy() {
if (channel != null) {
channel.close();
}
worker.shutdownGracefully();
boss.shutdownGracefully();
log.info("Netty server shutdown success");
}
}
重点
NettyServerHandler
package com.springboot.exam.controller.netty;
import com.springboot.exam.service.LoginService;
import com.springboot.exam.util.DemoHandler;
import com.springboot.exam.util.util;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//存储全局
public static final Map<Object,ChannelHandlerContext> mapsocket = new HashMap<>();
// 读取数据事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf bufferBuf = (ByteBuf) msg;
byte[] data = new byte[bufferBuf.readableBytes()];
bufferBuf.readBytes(data);
System.out.println("解密前");
for (byte sss:data) {
System.out.print(sss+" ");
}
System.out.println();
//业务逻辑
//记得一定要存ChannelHandlerContext
//后期要通过ChannelHandlerContext 发消息给固定的机器
mapsocket.put("发送信息过来的机器的主键id",ctx);
}
//发送消息
//xxs 发送的消息(指令)
//ss 发送信息过来的机器的主键id(可以用其他主键代替,只要和上面对应)
public static void pudate(byte[] xxs,int ss){
ByteBuf msg = null;
msg = Unpooled.buffer(xxs.length);
msg.writeBytes(xxs);
mapsocket.get(ss).writeAndFlush(msg);
}
public static byte[] bl_zjl="000000".getBytes();
public byte[] getBl_zjl() {
return bl_zjl;
}
@Autowired
private LoginService loginServers;
// 数据读取完毕事件
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
/*byte[] req = getBl_zjl();
ByteBuf msg = null;
msg = Unpooled.buffer(req.length);
msg.writeBytes(req);
ctx.writeAndFlush(msg);*/
}
// 异常发生事件
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close(); //关闭上下文,上下文是所有信息的汇总
super.exceptionCaught(ctx, cause);
System.out.println("异常了!!!!!!!!!!!!!!!");
}
/**
* 将指定byte数组以16进制的形式打印到控制台
*
* @param hint
* String
* @param b
* byte[]
* @return void
*/
public static void printHexString(String hint, byte[] b) {
System.out.print(hint);
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
System.out.print(hex.toUpperCase() + " ");
}
System.out.println("");
}
/**
*
* @param b
* byte[]
* @return String
*/
public String Bytes2HexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += " 0x" + hex.toUpperCase();
}
return ret;
}
/**
* 将两个ASCII字符合成一个字节; 如:"EF"–> 0xEF
*
* @param src0
* byte
* @param src1
* byte
* @return byte
*/
public byte uniteBytes(byte src0, byte src1) {
byte _b0 = Byte.decode("0x" + new String(new byte[] {src0})).byteValue();
_b0 = (byte) (_b0 << 4);
byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 })).byteValue();
byte ret = (byte) (_b0 ^ _b1);
return ret;
}
/**
* 将指定字符串src,以每两个字符分割转换为16进制形式 如:"2B44EFD9" –> byte[]{0x2B, 0×44, 0xEF,
* 0xD9}
*
* @param src
* String
* @return byte[]
*/
public byte[] HexString2Bytes(String src) {
if (null == src || 0 == src.length()) {
return null;
}
byte[] ret = new byte[src.length() / 2];
byte[] tmp = src.getBytes();
for (int i = 0; i < (tmp.length / 2); i++) {
ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
}
return ret;
}
public static final Map<Object,String> sdsf = new HashMap<>();
public static void main(String[] args){
sdsf.put("11","123");
sdsf.put("22","123456");
sdsf.put("23","123456789");
System.out.println(sdsf.get("11"));
System.out.println(sdsf.get("22"));
System.out.println(sdsf.get("23"));
}
}
NettyClientHandler.java
package com.springboot.exam.controller.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
// 通道就绪事件
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client: "+ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("00 0D BC BB BC BC BF AC 82 DD 87 5C C9 01 05", CharsetUtil.UTF_8));
ctx.writeAndFlush(Unpooled.copiedBuffer("00 0D BC BB BC BC BF AC 82 DD 87 5C C9 01 05", CharsetUtil.UTF_8));
}
// 读取数据事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
System.out.println("服务器端发来的消息:"+buf.toString(CharsetUtil.UTF_8));
}
}
NettyClient.java
package com.springboot.exam.controller.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyClient {
public static void main(String[] args) throws Exception {
// 1. 创建一个线程组
EventLoopGroup group = new NioEventLoopGroup();
// 2. 创建客户端的启动助手,完成相关配置
Bootstrap b = new Bootstrap();
b.group(group) // 3.设置线程组
.channel(NioSocketChannel.class) //4.设置客户端通道的实现类
.handler(new ChannelInitializer<SocketChannel>() {// 5.创建一个通道初始化对象
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyClientHandler());//6.往pipeline链中添加自定义的handler
}
});
System.out.println("........Client is ready............");
// 7.启动客户端去连接服务器端,connect方法是异步的 ,sync方法是同步阻塞的
ChannelFuture cf = b.connect("127.0.0.1",10010).sync();
// 8.关闭连接(异步非阻塞)
cf.channel().closeFuture().sync();
}
}
Application.java
package com.springboot.exam;
import com.springboot.exam.controller.netty.NettyServer;
import io.netty.channel.ChannelFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.CommandLineRunner;
/**
* ClassName: SpringBootApplication
* description:
* author:
* date: 2018-09-30 09:15
**/
@org.springframework.boot.autoconfigure.SpringBootApplication//@EnableAutoConfiguration @ComponentScan
public class ExamApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ExamApplication.class, args);
}
@Autowired
NettyServer nettyServer;
@Override
public void run(String... args) throws Exception {
ChannelFuture start = nettyServer.start();
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
nettyServer.destroy();
}
});
start.channel().closeFuture().syncUninterruptibly();
}
}
最后推荐一个测试Netty的工具 https://download.csdn.net/download/qq_34775102/11830134
如果你觉得这篇内容对你挺有启发请点赞+关注
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?