**使用java编码的springboot项目在调用C语言等其他语言编写的Tcp接口时,使用netty框架可以实现数据双向持续交互处理。
注:在交互过程中,c语言生成的二进制字节码转java字符串时往往出现乱码,请看后面处理方式(netty处理类中的代码)。**
一、引入netty的jar包
io.netty
netty-all
二、使用netty框架
1、创建客户端
package com.iflytek.digtal.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
-
netty客户端
*/
public class NettyClient {/**
-
连接tcp服务端初始化
-
@throws InterruptedException
*/
public static void init() throws InterruptedException {
//客户端需要一个事件循环组
NioEventLoopGroup group = new NioEventLoopGroup();try {
//创建客户端启动对象
//注意客户端使用的不是SocketBootstrap而是Bootstrap
Bootstrap bootstrap = new Bootstrap();// 设置相关参数 bootstrap.group(group) //设置线程组 .channel(NioSocketChannel.class)// 使用NioSocketChannel作为客户端的通道实现 .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyClientHandler()); } }); System.out.println("netty client start.."); ChannelFuture cf = bootstrap.connect("127.0.0.1", 6000).sync(); cf.channel().closeFuture().sync();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
//客户端需要一个事件循环组
NioEventLoopGroup group = new NioEventLoopGroup();try { //创建客户端启动对象 //注意客户端使用的不是SocketBootstrap而是Bootstrap Bootstrap bootstrap = new Bootstrap(); // 设置相关参数 bootstrap.group(group) //设置线程组 .channel(NioSocketChannel.class)// 使用NioSocketChannel作为客户端的通道实现 .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyClientHandler()); } }); System.out.println("netty client start.."); ChannelFuture cf = bootstrap.connect("127.0.0.1", 6000).sync(); cf.channel().closeFuture().sync(); }finally { group.shutdownGracefully(); }
}
-
}
2、创建服务端
package com.iflytek.digtal.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;
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//创建两个线程组bossGroup和workerGroup,含有的子线程NioEventLoop的个数默认是CPU的两倍
//bossGroup只是处理连接请求,真正的和客户端业务处理,会交给workerGroup完成
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
try {
//创建服务器端的启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
//使用链式编程来配置参数
bootstrap.group(bossGroup, workerGroup)//设置两个线程组
.channel(NioServerSocketChannel.class)//使用NioServerSocketChannel作为服务器的通道实现
//初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接
//多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//对workerGroup的SocketChannel设置处理器
channel.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("netty server start..");
//绑定一个端口并且同步生成一个ChannelFuture异步对象,通过isDone()等方法可以判断异步事件的执行情况
//启动服务器(并绑定的端口),bind是异步操作,sync方法是等待异步操作执行完毕
ChannelFuture cf = bootstrap.bind(9000).sync();
//给cf注册监听器,监听我们关心的事件
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (cf.isSuccess()) {
System.out.println("监听端口9000成功");
} else {
System.out.println("监听端口9000失败");
}
}
});
//等待服务端监听端口关闭,closeFuture是异步操作
//通过sync方法同步等待通道关闭处理完毕,这里会阻塞等待通道关闭完成,内部调用的是Object的wait()方法
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
3、编写处理类
package com.iflytek.digtal.netty2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
-
处理类
*/
@Slf4j
public class NettyClientHandler2 extends ChannelInboundHandlerAdapter {/**
- 客户端连接标识
- @param ctx
- @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = Unpooled.copiedBuffer(“HelloServer”.getBytes(Charset.forName(“GBK”)));
ctx.writeAndFlush(buf);
}
//当通道建立后有事件时会触发,即服务端发送数据给客户端
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg; log.info("哈哈哈:{}",msg); // 复制内容到字节数组bytes byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); log.info("收到的数据为:{}", bytes); int[] lengthArr = {4,4,4,4,4,4,56,24,56,24,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,56,32,8}; String[] strArr = {"int","int","int","int","int","int","char","char","char","char", "float","float","int","int","float","float","float","float","int","int","int","int", "int","int","int","int","char","char","systime"}; String[] strs = new String[29]; int offset = 0; for(int i =0;i<29;i++){ byte[] bytes1 = new byte[lengthArr[i]]; System.arraycopy(bytes, offset, bytes1, 0, lengthArr[i]); offset = offset + lengthArr[i]; if("int".equals(strArr[i])){ strs[i] = String.valueOf(bytesToInt(bytes1,0)); }else if("char".equals(strArr[i])){ strs[i] = bytesToChar(bytes1); }else if("float".equals(strArr[i])){ strs[i] = String.valueOf(byteToFloat(bytes1,0)); }else if("systime".equals(strArr[i])){ strs[i] = bytesToChar(bytes1); } } System.out.println("二进制数据转化后的结果:"+ Arrays.toString(strs)); // 前六个int数值 String[] str = new String[6]; for(int i =0,j=0;j<24;i++){ str[i] = String.valueOf(bytesToInt(bytes,j)); j = j+4; } System.out.println("str:"+ Arrays.toString(str)); //第七个试验名称 byte[] bytes1 = new byte[56]; System.arraycopy(bytes, 24, bytes1, 0, 56);
// for(int i=0;i<56;i++){
// bytes1[i] = bytes[20+i];
// }
log.info(“试验名称字节数组为:{}”, bytes1);
String str1 = bytesToChar(bytes1);
System.out.println(“试验名称str1:”+str1);
//第八个通道名称
byte[] bytes2 = new byte[24];
System.arraycopy(bytes, 80, bytes2, 0, 24);
// for(int i=0;i<56;i++){
// bytes1[i] = bytes[20+i];
// }
log.info(“通道名称字节数组为:{}”, bytes2);
String str2 = bytesToChar(bytes2);
System.out.println(“通道名称str2:”+str2);
//第九个采集名称
byte[] bytes3 = new byte[56];
System.arraycopy(bytes, 104, bytes3, 0, 56);
// for(int i=0;i<56;i++){
// bytes1[i] = bytes[20+i];
// }
log.info(“采集名称字节数组为:{}”, bytes3);
String str3 = bytesToChar(bytes3);
System.out.println(“采集名称str3:”+str3);
System.out.println("服务端地址是:" + ctx.channel().remoteAddress());
}
public int combine(byte b[])
{
int t1=(b[3]&0xff)<<24;
int t2=(b[2]&0xff)<<16;
int t3=(b[1]&0xff)<<8;
int t4=b[0]&0xff;
//System.out.println(b[1]&0xff);//输出的是一个整形数据
//在java中,设计int和比int位数来的小的类型b,如byte,char等,都是先把小类型扩展成int再来运算,
//return( t1<<24)+(t2<<16)+(t3<<8)+t4;//必须加括号
return t1+t2+t3+t4;
}
private char[] getChars (byte[] bytes) {
Charset cs = Charset.forName ("GBK");
ByteBuffer bb = ByteBuffer.allocate (bytes.length);
bb.put (bytes);
bb.flip ();
CharBuffer cb = cs.decode (bb);
return cb.array();
}
/**
* byte转int
* @Title: bytesToInt
*
* @param: @param src 源数组
* @param: @param offset 起始偏移量
* @return: int
*/
public int bytesToInt(byte[] src, int offset) {
int value;
value = src[offset] & 0xFF;
value |= ((long) (src[offset + 1] & 0xFF) << 8);
value |= ((long) (src[offset + 2] & 0xFF) << 16);
value |= ((long) (src[offset + 3] & 0xFF) << 24);
return value;
}
public String bytesToChar(byte[] bytes){
char[] chars = new char[bytes.length];
for (int i = 0; i < bytes.length; i++) {
chars[i] = (char) (bytes[i] & 0xFF); // 使用位运算将字节转换为字符
}
// for(int i =0;i<chars.length;i++){
// System.out.println(“char数组:”+chars[i]);
// }
String str = new String(chars);
// System.out.println(“转换后的字符数组:” + str);
return str;
}文章来源:https://www.toymoban.com/news/detail-831142.html
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.info("关闭通道");
cause.printStackTrace();
ctx.close();
}
/**
* byte转long
* @Title: bytesToLong
*
* @param: @param src 源数组
* @param: @param offset 起始偏移量
* @return: long
*/
public static long bytesToLong(byte[] src, int offset) {
long value = 0;
for(int i = 0; i < 8; i++) {
value |= ((long) (src[offset + i] & 0xFF) << (8 * i));
}
return value;
}
/**
* byte转float
* @Title: byteToFloat
*
* @param: @param src 源数组
* @param: @param offset 起始偏移量
* @return: float
*/
public static float byteToFloat(byte[] src, int offset) {
int value;
value = src[offset + 0] & 0xFF;
value |= ((long) (src[offset + 1] & 0xFF) << 8);
value |= ((long) (src[offset + 2] & 0xFF) << 16);
value |= ((long) (src[offset + 3] & 0xFF) << 24);
return Float.intBitsToFloat(value);
}
/**
* byte转double
* @Title: byteToDouble
*
* @param: @param src 源数组
* @param: @param offset 起始偏移量
* @return: double
*/
public static double byteToDouble(byte[] src, int offset) {
long value = 0;
for (int i = 0; i < 8; i++) {
value |= ((long) (src[offset + i] & 0xff)) << (8 * i);
}
return Double.longBitsToDouble(value);
}
public static void main(String[] args){
File file = new File("C:\\Users\\Administrator\\Desktop\\test.txt"); // 要读取的文件名
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GBK"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
try{
String str = "hello";
byte[] bytes = str.getBytes("GBK"); // 将字符串转换为字节数组
String newStr = new String(bytes, "GBK"); // 将字节数组转换为字符串
System.out.println("源字符串:"+str);
}catch (Exception e){
e.printStackTrace();
}
}
}
注:其中c语言二进制字节码int、double、float型通过位运算皆可;
而string类型转换的时候存在乱码,此时我是通过通配符替换掉乱码的,就是除了中文、数字、特殊字符一律替换为空;
另外还有一个问题,就是netty的channel循环接收时,上一轮的字节数的最后不是字符的最后一位,这样下一轮的开头其实是一个字符剩下的部分,这时拼接转写也会出现为空的情况,这时java小端字节码编码方式造成的,只要规避即可。文章来源地址https://www.toymoban.com/news/detail-831142.html
到了这里,关于Springboot使用Netty连接Tcp接口(c语言二进制字节码转java字符串)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!