JAVA实现JT809客户端模拟数据和服务端接受数据

最近在项目中遇到对接JT808和JT809,其中遇到很多问题,首先因为对这两个协议不熟悉,准备在网上搜资料查询,有没有开源的关于JT808和809协议的项目,发现JT808的已经有开源的,而对于JT809的好像没有。经过两天自己查询资料,然后说下JT809,首先,不管是对接别人的数据还是接受下级的数据,都需要自己去按照官方定义的协议。先说下客户端,选择Netty3.10.6,以下是pom.xml配置文件



    4.0.0

    jt809client
    jt809clientdemol
    1.0-SNAPSHOT

    
        
            org.slf4j
            slf4j-api
            1.7.24
        
        
            ch.qos.logback
            logback-classic
            1.0.13
        
        
            org.apache.commons
            commons-lang3
            3.6
        
        
    

以下是客户端的代码:

TcpClient.java

package netty.client;

import handler.Decoder;
import handler.HeartBeatHandler;
import handler.RecevieHandler;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.timeout.IdleStateHandler;
import org.jboss.netty.util.HashedWheelTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class TcpClient {

    private static final Logger LOG = LoggerFactory.getLogger(TcpClient.class);

    private static final int DEFAULT_PORT = 8001;

    private long connectTimeoutMillis = 3000;

    private int port = DEFAULT_PORT;

    private boolean tcpNoDelay = false;

    private boolean reuseAddress = true;

    private boolean keepAlive = true;

    private int workerCount = 4;

    private ClientBootstrap bootstrap = null;

    private static Channel channel = null;

    private Executor bossExecutor = Executors.newCachedThreadPool();

    private Executor workerExecutor = Executors.newCachedThreadPool();


    private static TcpClient instance = new TcpClient();

    private TcpClient() {
        init();
    }

    public static TcpClient getInstence() {
        return instance;
    }


    public void init() {

        bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(
                bossExecutor, workerExecutor, workerCount));
        bootstrap.setOption("tcpNoDelay", tcpNoDelay);
        bootstrap.setOption("connectTimeoutMillis", connectTimeoutMillis);
        bootstrap.setOption("reuseAddress", reuseAddress);
        bootstrap.setOption("keepAlive", keepAlive);
    }


    public Channel getChannel(String address, int port) {

        if (null == channel || !channel.isOpen()) {
            bootstrap.setOption("writeBufferHighWaterMark", 64 * 1024);
            bootstrap.setOption("writeBufferLowWaterMark", 32 * 1024);
            bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
                //@Override
                public ChannelPipeline getPipeline() throws Exception {
                    ChannelPipeline pipeline = Channels.pipeline();
//					pipeline.addLast("loging", new LoggingHandler(InternalLogLevel.ERROR)); 打印日志信息,上线稳定后可去掉
                    pipeline.addLast("timeout", new IdleStateHandler(new HashedWheelTimer(), 10, 60, 0));//设置空闲心跳机制
                    pipeline.addLast("heartbeat", new HeartBeatHandler());//心跳发送包处理handler
                    pipeline.addLast("decode", new Decoder());//解码
                    pipeline.addLast("loginHandler", new RecevieHandler());//反馈数据处理
                    return pipeline;
                }
            });
            ChannelFuture future = bootstrap.connect(new InetSocketAddress(
                    address, port));
            future.awaitUninterruptibly();
            if (future.isSuccess()) {
                channel = future.getChannel();
            } else {
                //throw new Exception(future.getCause());
            }
        }

        return channel;
    }


    public long getConnectTimeoutMillis() {
        return connectTimeoutMillis;
    }

    public void setConnectTimeoutMillis(long connectTimeoutMillis) {
        this.connectTimeoutMillis = connectTimeoutMillis;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public boolean isTcpNoDelay() {
        return tcpNoDelay;
    }

    public void setTcpNoDelay(boolean tcpNoDelay) {
        this.tcpNoDelay = tcpNoDelay;
    }

    public boolean isReuseAddress() {
        return reuseAddress;
    }

    public void setReuseAddress(boolean reuseAddress) {
        this.reuseAddress = reuseAddress;
    }

    public boolean isKeepAlive() {
        return keepAlive;
    }

    public void setKeepAlive(boolean keepAlive) {
        this.keepAlive = keepAlive;
    }

    public int getWorkerCount() {
        return workerCount;
    }

    public void setWorkerCount(int workerCount) {
        this.workerCount = workerCount;
    }

}
TcpClient809.java
package netty.client;

import bean.Idc2AwsGpsVo;
import bean.Message;
import org.apache.commons.lang3.StringUtils;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import utill.CRC16CCITT;
import utill.Constants;
import utill.JT809Constants;

import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Date;

public class TcpClient809 {

    private static Logger LOG = LoggerFactory.getLogger(TcpClient809.class);
    public static int PLANT_CODE;//公司介入码
    public static int ZUCHE_ID_FUJIAN;//公司用户名
    public static String ZUCHE_PWD_FUJIAN;
    public static String LONGINSTATUS = "";
    public static String LOGINING = "logining";
    private static int LOGIN_FLAG = 0;
    private static String DOWN_LINK_IP = "127.0.0.1";//初始化基类
    private static TcpClient tcpClient = TcpClient.getInstence();//初始化
    private static TcpClient809 tcpClient809 = new TcpClient809();
    //初始化channel,以便心跳机制及时登录
    private Channel channel = tcpClient.getChannel(Constants.TCP_ADDRESS, Constants.TCP_PORT);

    public static TcpClient809 getInstance() {
        String localIp = "127.0.0.1";
        if (StringUtils.isNotBlank(localIp)) {
            PLANT_CODE = 11;
            ZUCHE_ID_FUJIAN = 11;
            ZUCHE_PWD_FUJIAN = "";
        } else {
            LOG.error("获取本机IP异常");
        }
        return tcpClient809;
    }

    /**
     * 判断是否登录 * boolean * @return
     */
    public boolean isLogined() {
        return Constants.LOGIN_STATAUS.equals(LONGINSTATUS); //Constants常量类,自己随便定义就好,LOGIN_STATAUS="0x00"
    }

    /**
     * 登录接入平台 * boolean * @return
     */
    public boolean login2FuJianGov() {
        boolean success = false;
        if (!Constants.LOGIN_STATAUS.equals(LONGINSTATUS) && !LOGINING.equals(LONGINSTATUS)) {
            //开始登录 Message为数据对象,代码稍后给出
            Message msg = new Message(JT809Constants.UP_CONNECT_REQ);
            ChannelBuffer buffer = ChannelBuffers.buffer(46);
            buffer.writeInt(ZUCHE_ID_FUJIAN);
            byte[] pwd = getBytesWithLengthAfter(8, ZUCHE_PWD_FUJIAN.getBytes());
            buffer.writeBytes(pwd);
            byte[] ip = getBytesWithLengthAfter(32, DOWN_LINK_IP.getBytes());
            buffer.writeBytes(ip);
            buffer.writeShort((short) 8091);//不明白这是什么
            msg.setMsgBody(buffer);
            channel = tcpClient.getChannel(Constants.TCP_ADDRESS, Constants.TCP_PORT);
            channel.write(buildMessage(msg));
            LONGINSTATUS = LOGINING;
        }
        return success;
    }

    public static ChannelBuffer buildMessage(Message msg) {
        int bodyLength = 0;
        if (null != msg.getMsgBody()) {
            bodyLength = msg.getMsgBody().readableBytes();
        }
        msg.setMsgGesscenterId(PLANT_CODE);
        ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(bodyLength + Message.MSG_FIX_LENGTH);
        ChannelBuffer headBuffer = ChannelBuffers.buffer(22);//---数据头
        headBuffer.writeInt(buffer.capacity() - 1);
        headBuffer.writeInt(msg.getMsgSn());
        headBuffer.writeShort((short) msg.getMsgId());
        headBuffer.writeInt(msg.getMsgGesscenterId());
        headBuffer.writeBytes(msg.getVersionFlag());
        headBuffer.writeByte(0);
        headBuffer.writeInt(10);
        buffer.writeBytes(headBuffer);//---数据体
        if (null != msg.getMsgBody()) {
            buffer.writeBytes(msg.getMsgBody());
        }
        ChannelBuffer finalBuffer = ChannelBuffers.copiedBuffer(buffer);//--crc校验码
        byte[] b = ChannelBuffers.buffer(finalBuffer.readableBytes()).array();
        finalBuffer.getBytes(0, b);
        int crcValue = CRC16CCITT.crc16(b);
        finalBuffer.writeShort((short) crcValue);//2//转义
        byte[] bytes = ChannelBuffers.copiedBuffer(finalBuffer).array();
        ChannelBuffer headFormatedBuffer = ChannelBuffers.dynamicBuffer(finalBuffer.readableBytes());
        formatBuffer(bytes, headFormatedBuffer);
        ChannelBuffer buffera = ChannelBuffers.buffer(headFormatedBuffer.readableBytes() + 2);
        buffera.writeByte(Message.MSG_HEAD);
        buffera.writeBytes(headFormatedBuffer);
        buffera.writeByte(Message.MSG_TALL);
        return ChannelBuffers.copiedBuffer(buffera);
    }

    /**
     * 发送数据到接入平台 * boolean * @param awsVo 是上层程序得到的带发送的数据对象,可以看自己的需求,替换 * @return
     */
    public boolean sendMsg2FuJianGov(Idc2AwsGpsVo awsVo) {
        boolean success = false;
        if (isLogined()) {
            //已经登录成功,开始发送数据
            LOG.info("开始发送数据");
            channel = tcpClient.getChannel(Constants.TCP_ADDRESS, Constants.TCP_PORT);
            if (null != channel && channel.isWritable()) {
                Message msg = buildSendVO(awsVo);
                ChannelBuffer msgBuffer = buildMessage(msg);
                channel.write(msgBuffer);
            } else {
                LONGINSTATUS = "";
            }
        } else if (
                LOGIN_FLAG == 0) {
            LOGIN_FLAG++;
            login2FuJianGov();
            LOG.error("--------------第一次登录");
        } else {
            LOG.error("--------------等待登录");
        }
        return success;
    }

    /**
     * 转换VO * void * @param awsVo
     */
    private Message buildSendVO(Idc2AwsGpsVo awsVo) {
        Message msg = new Message(JT809Constants.UP_EXG_MSG);
        ChannelBuffer buffer = ChannelBuffers.buffer(36);//是否加密
        buffer.writeByte((byte) 0);//0未加密 // 1//日月年dmyy
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        buffer.writeByte((byte) cal.get(Calendar.DATE));
        buffer.writeByte((byte) (cal.get(Calendar.MONTH) + 1));
        String hexYear = "0" + Integer.toHexString(cal.get(Calendar.YEAR));
        buffer.writeBytes(hexStringToByte(hexYear));//4//时分秒
        buffer.writeByte((byte) cal.get(Calendar.HOUR));
        buffer.writeByte((byte) cal.get(Calendar.MINUTE));
        buffer.writeByte((byte) cal.get(Calendar.SECOND));//3//经度,纬度
        buffer.writeInt(formatLonLat(awsVo.getLon()));//4
        buffer.writeInt(formatLonLat(awsVo.getLat()));//4//速度
        buffer.writeShort(awsVo.getSpeed());//2//行驶记录速度
        buffer.writeShort(awsVo.getSpeed());//2//车辆当前总里程数
        buffer.writeInt(awsVo.getMileage());//4//方向
        buffer.writeShort(awsVo.getDirection());//2//海拔
        buffer.writeShort((short) 0);//2//车辆状态buffer.writeInt(1);//4//报警状态
        buffer.writeInt(0);//0表示正常;1表示报警//4
        ChannelBuffer headBuffer = ChannelBuffers.buffer(buffer.capacity() + 28);
        headBuffer.writeBytes(getBytesWithLengthAfter(21, awsVo.getVehicleNo().getBytes()));//21 车牌号
        headBuffer.writeByte((byte) 1);//1 车牌颜色:注意不是车身颜色
        headBuffer.writeShort(JT809Constants.UP_EXG_MSG_REAL_LOCATION);//2 子业务码
        headBuffer.writeInt(buffer.capacity());
        headBuffer.writeBytes(buffer);
        msg.setMsgBody(headBuffer);
        return msg;
    }

    /**
     * 报文转义 * void * @param bytes * @param formatBuffer
     */
    private static void formatBuffer(byte[] bytes, ChannelBuffer formatBuffer) {
        for (byte b : bytes) {
            switch (b) {
                case 0x5b:
                    byte[] formatByte0x5b = new byte[2];
                    formatByte0x5b[0] = 0x5a;
                    formatByte0x5b[1] = 0x01;
                    formatBuffer.writeBytes(formatByte0x5b);
                    break;
                case 0x5a:
                    byte[] formatByte0x5a = new byte[2];
                    formatByte0x5a[0] = 0x5a;
                    formatByte0x5a[1] = 0x02;
                    formatBuffer.writeBytes(formatByte0x5a);
                    break;
                case 0x5d:
                    byte[] formatByte0x5d = new byte[2];
                    formatByte0x5d[0] = 0x5e;
                    formatByte0x5d[1] = 0x01;
                    formatBuffer.writeBytes(formatByte0x5d);
                    break;
                case 0x5e:
                    byte[] formatByte0x5e = new byte[2];
                    formatByte0x5e[0] = 0x5e;
                    formatByte0x5e[1] = 0x02;
                    formatBuffer.writeBytes(formatByte0x5e);
                    break;
                default:
                    formatBuffer.writeByte(b);
                    break;
            }
        }
    }

    /**
     * 16进制字符串转换成byte数组 * byte[] * @param hex
     */
    public static byte[] hexStringToByte(String hex) {
        hex = hex.toUpperCase();
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }

    private static byte toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }

    /**
     * 格式化经纬度,保留六位小数 * int * @param needFormat * @return
     */
    private int formatLonLat(Double needFormat) {
        NumberFormat numFormat = NumberFormat.getInstance();
        numFormat.setMaximumFractionDigits(6);
        numFormat.setGroupingUsed(false);
        String fristFromat = numFormat.format(needFormat);
        Double formatedDouble = Double.parseDouble(fristFromat);
        numFormat.setMaximumFractionDigits(0);
        String formatedValue = numFormat.format(formatedDouble * 1000000);
        return Integer.parseInt(formatedValue);
    }

    /**
     * 补全位数不够的定长参数 有些定长参数,实际值长度不够,在后面补0x00 * byte[] * @param length * @param pwdByte * @return
     */
    private byte[] getBytesWithLengthAfter(int length, byte[] pwdByte) {
        byte[] lengthByte = new byte[length];
        for (int i = 0; i < pwdByte.length; i++) {
            lengthByte[i] = pwdByte[i];
        }
        for (int i = 0; i < (length - pwdByte.length); i++) {
            lengthByte[pwdByte.length + i] = 0x00;
        }
        return lengthByte;
    }

    public static void main(String[] args) {
        TcpClient809 s = TcpClient809.getInstance();
        Idc2AwsGpsVo awsVo = new Idc2AwsGpsVo();
        awsVo.setDirection((short) 12);
        awsVo.setLon(117.2900911);
        awsVo.setLat(39.56362);
        awsVo.setSpeed((short) 45);
        awsVo.setMileage(10001);
        awsVo.setVehicleNo("幽123D32");
        LOG.info("开始send message");
        s.sendMsg2FuJianGov(awsVo);
        try {
            Thread.sleep(20 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        s.sendMsg2FuJianGov(awsVo);
    }
}

 

上述是客户端登陆,模拟下级平台的登陆,心跳包,还有就是上报车子的GPS信息到上级平台。

 

以下地址是csdn下载连接地址。

https://download.csdn.net/download/yanchangyufan/10567788

 

你可能感兴趣的:(心得,JAVA学习心得,Netty学习)