Springboot使用Netty连接Tcp接口(c语言二进制字节码转java字符串)

   **使用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;
}

@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小端字节码编码方式造成的,只要规避即可。

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 SpringBoot 整合 Netty 实现 TCP 服务器可以让我们更方便地管理和部署我们的应用程序。下面是一些基本的步骤: 1. 创建一个 SpringBoot 项目,并添加 Netty 和相应的依赖。 2. 创建一个 Netty 服务类,实现 ChannelInboundHandlerAdapter 接口。在这个类中,你可以实现接收、处理和发送 TCP 消息的逻辑。 3. 通过 SpringBoot 的配置文件,配置 Netty 服务器的端口和其他参数。 4. 在 SpringBoot 的启动类中,使用 @Bean 注解将 Netty 服务类注册为 bean。 5. 启动 SpringBoot 应用程序,Netty 服务器将开始监听传入的连接。 下面是一个简单的示例: ``` // 服务类 @Component public class MyNettyServer extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 处理接收到的消息 ByteBuf buf = (ByteBuf) msg; String message = buf.toString(CharsetUtil.UTF_8); // 返回响应消息 String response = "Hello, " + message; ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes())); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 处理异常 cause.printStackTrace(); ctx.close(); } } // 启动类 @SpringBootApplication public class MyApplication { @Autowired private MyNettyServer myNettyServer; @Value("${netty.port}") private int port; @PostConstruct public void start() throws Exception { // 创建 EventLoopGroup EventLoopGroup group = new NioEventLoopGroup(); try { // 创建 ServerBootstrap ServerBootstrap b = new ServerBootstrap(); b.group(group) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { // 添加处理器 ch.pipeline().addLast(myNettyServer); } }); // 启动服务器 ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); } finally { // 关闭 EventLoopGroup group.shutdownGracefully().sync(); } } public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } // 配置文件 netty.port=8080 ``` 在这个例子中,我们创建了一个名为 `MyNettyServer` 的服务类,并实现了 `ChannelInboundHandlerAdapter` 接口。在 `channelRead` 方法中,我们处理接收到的消息,并返回响应消息。在 `exceptionCaught` 方法中,我们处理异常。 在启动类中,我们使用 `@Autowired` 注解将 `MyNettyServer` 注入到启动类中,并使用 `@Value` 注解获取配置文件中的端口号。在 `start` 方法中,我们创建了一个 `EventLoopGroup`,并使用 `ServerBootstrap` 创建了一个 Netty 服务器。然后,我们将 `MyNettyServer` 添加到 `SocketChannel` 的处理器中。最后,我们启动服务器,并在关闭服务器之前等待连接。 这只是一个简单的示例,你可以根据你的需求修改和扩展它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值