首页 > 其他分享 >netty发送socket短连接请求,自定义报文头

netty发送socket短连接请求,自定义报文头

时间:2023-09-18 16:57:20浏览次数:40  
标签:netty 加密 socket 自定义 报文 ctx msg 数据包 channel

package com.chinaums.japi.util;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.ReferenceCountUtil;

import java.nio.charset.Charset;

public class NettyClient {
    public static void main(String[] args) {
        String req = "<ap><ProductID>IBB</ProductID><ChannelType>ERP</ChannelType><CorpNo/><OpNo/><AuthNo/><ReqSeqNo>TER1694770206768</ReqSeqNo><ReqDate>20230915</ReqDate><ReqTime>173006</ReqTime><Sign/><CCTransCode>ABCDEF</CCTransCode><Amt/><Corp/><Cmp/></ap>";
        try {
            String ret = sendXmlToIct("172.16.16.207", 19999, req);
            System.out.println(ret);
        } catch (InterruptedException e) {
            System.out.println("异常了");
        }
    }

    /**
     * 发送XML报文到ICT
     * 调用该方法时不需要关心报文头,处理器会自动处理
     *
     * @param ip
     * @param port
     * @param xml
     * @return
     * @throws InterruptedException
     */
    public static String sendXmlToIct(String ip, int port, String xml) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 添加处理报文头的 ChannelHandler
                            pipeline.addLast(new MyHeaderReqHandler());
                            pipeline.addLast(new MyHeaderResHandler());
                        }
                    });
            Channel channel = bootstrap.connect(ip, port).sync().channel();
            channel.writeAndFlush(Unpooled.copiedBuffer(xml, Charset.forName("GBK")));
            // 等待消息发送完成
            channel.flush();
            StringBuilder messageBuilder = new StringBuilder();
            channel.pipeline().addLast(new SimpleChannelInboundHandler<ByteBuf>() {
                @Override
                protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
                    messageBuilder.append(msg.toString(Charset.forName("GBK")));
                }

                @Override
                public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                    super.channelReadComplete(ctx);
                    ctx.channel().close();
                }

                @Override
                public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                    super.exceptionCaught(ctx, cause);
                    // 处理异常
                }
            });
            channel.closeFuture().sync();
            return messageBuilder.toString();
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 发送请求前报文头处理
     * 这个也可以单独弄出去,而不是现在这样的内部类
     * <p>
     * Socket方式中报文结构为“包头+数据包”。
     * 包头固定为7个字节长,第1字节为是否加密标志(0-不加密,1-加密)。
     * 后6个字节是数据包的长度,即将报文长度直接转为字符串存储,长度不足6位则右边用空格补足,
     * 比如:“1234  ”。比如汇兑的长度为1234字节的数据包,其包头为“01234  ”共7位,其中数据包长度包含加密包标志位。
     * 由于加密需要双方约定专门的加密算法,因此一般ERP送的加密标志都为0-不加密。
     */
    public static class MyHeaderReqHandler extends ChannelOutboundHandlerAdapter {
        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            if (msg instanceof ByteBuf) {
                ByteBuf data = (ByteBuf) msg;
                // 获取数据包长度
                int length = data.readableBytes();
                // 创建一个新的ByteBuf来存储带有报文头的完整数据
                ByteBuf newData = ctx.alloc().buffer(7 + length);
                // 写入加密标志(0-不加密,1-加密)
                newData.writeByte(0); // 这里写入了0,表示不加密
                // 将数据包的长度转换为字符串,并用空格补足6位
                String lengthStr = String.format("%06d", length);
                newData.writeBytes(lengthStr.getBytes());
                // 写入原始数据包
                newData.writeBytes(data);
                // 替换原始消息为带有报文头的新消息
                ReferenceCountUtil.release(msg);
                msg = newData;
            }
            super.write(ctx, msg, promise);
        }
    }

    /**
     * 接收响应后报文头处理
     * 这个也可以单独弄出去,而不是现在这样的内部类
     * <p>
     * Socket方式中报文结构为“包头+数据包”。
     * 包头固定为7个字节长,第1字节为是否加密标志(0-不加密,1-加密)。
     * 后6个字节是数据包的长度,即将报文长度直接转为字符串存储,长度不足6位则右边用空格补足,
     * 比如:“1234  ”。比如汇兑的长度为1234字节的数据包,其包头为“01234  ”共7位,其中数据包长度包含加密包标志位。
     * 由于加密需要双方约定专门的加密算法,因此一般ERP送的加密标志都为0-不加密。
     */
    public static class MyHeaderResHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof ByteBuf) {
                ByteBuf data = (ByteBuf) msg;
                // 读取加密标志
                byte encryptFlag = data.readByte();
                // 读取数据包长度
                byte[] lengthBytes = new byte[6];
                data.readBytes(lengthBytes);
                int length = Integer.parseInt(new String(lengthBytes).trim());
                // 读取数据包内容
                ByteBuf packet = data.readBytes(length);
                // 将解析后的数据继续传递给下一个处理器处理
                // 使用retain()方法确保数据不会被提前释放
                ctx.fireChannelRead(packet.retain());
                // 释放原始消息
                ReferenceCountUtil.release(msg);
            }
        }
    }
}

标签:netty,加密,socket,自定义,报文,ctx,msg,数据包,channel
From: https://www.cnblogs.com/daen/p/17712383.html

相关文章

  • 自定义对象的of生成操作
    自定义对象的of生成逻辑依赖于lombok的注解@RequiredArgsConstructor(staticName="of")写法@Accessors(chain=true)@Setter@Getter@RequiredArgsConstructor(staticName="of")publicclassStudent{@NonNullprivateStringname;priva......
  • Kingbase ES 自定义聚合函数和一次改写案例
    文章概要:KES的SQL的语法暂时不兼容oracle的自定义聚合函数的创建语法和流程,但是可以使用KES已支持的语法改写。本文整理和简单解析了自定义聚合函数的原理和解读了范例代码。并根据客户代码进行了改写。一,oracle自定义聚合函数的简析oracle的自定义聚合函数需要实现4个ODCIAg......
  • 自定义注解@ValidValueList
    1、自定义注解@ValidValueList和验证器ValidValueListValidator来确保集合中的元素必须是在指定的值列表中。2、注解@ValidValueList允许你在字段或参数上标记一个集合,并为其提供一组有效的值。验证器ValidValueListValidator则用于检查集合中的元素是否都在指定的值列表......
  • 2023-09-18 hexo博客之如何自定义页面内容宽度==》在custom.styl中添加两行代码即可
    前言:我的hexo主题为hexo-theme-next 5.1.4版本。操作如下:打开你的博客名称\themes\hexo-theme-next\source\css\_variables,找到这个文件custom.styl,然后把下面代码添加进去:$main-desktop=1200px$content-desktop=1000px刷新页面即可见效。......
  • EasyCode自定义模板
    一、前言最近做了几个傻瓜式的CRUD模块,光调整EasyCode生成的代码格式就调整了半天,毫无意义,但又必不可少。于是,网上找了关于EasyCode自定义模板的文章,尝试自定义模板,从根本上解决代码格式调整的痛点。EasyCode是IDEA开发的一个代码生成插件,主要通过自定义模板(基......
  • vue 自定义全局弹窗组件
    问题描述:vue自定义类似elementUI的this.$confirm解决方案:通过vue的extend方法实现然后全局注入 代码实现: 展示的组件(就是最基本的vue组件代码)<!--*@Author:linchunlinchun*@Date:2023-09-1810:14:24*@LastEditors:linchunlinchun*@LastEdit......
  • Springboot简单功能示例-4 自定义加密进行登录验证
    springboot-sample介绍springboot简单示例-自定义加密进行登录验证跳转到发行版软件架构(当前发行版)Springboot3.1.3hutoolbcprov-jdk18on安装教程gitclone--branch自定义加密进行登录验证[email protected]:simen_net/springboot-sample.git主要功能使用SM2库......
  • 自定义Feign的配置
        ......
  • html5 的 webScoket 和 C# 建立Socket连接
    html5的webScoket和C#建立Socket连接最近使用的web项目中,需要服务器直接触发前端显示效果。所以研究了一下websocket:名词解释:WebSocketWebSocket协议是一种双向通信协议,它建立在TCP之上,同http一样通过TCP来传输数据,但是它和http最大的不同有两点:1.WebSocket是一种双向......
  • destoon添加自定义字段报错INSERT INTO [pre]fields
     今天做destoon开发时候在后台添加自定义字段时候出现:destoon7.0-8.0添加自定义字段报错MySQLQuery:INSERTINTO[pre]fields(tb,name,title,note,type,length,html,default_value,option_value,width,height,input_limit,addition,search,display,front)VALUES('article_21',......