業(yè)務(wù)場(chǎng)景:跟設(shè)備通信玷氏,不同的廠家有不同的協(xié)議,這時(shí)后臺(tái)服務(wù)要兼容腋舌,比如說(shuō)盏触,設(shè)備A使用的是String字符串,設(shè)備B使用的是byte[]块饺,這時(shí)候該怎么處理呢赞辩,使用自定義解碼器,去識(shí)別是String授艰,還是byte[]辨嗽,然后轉(zhuǎn)發(fā)給相應(yīng)的業(yè)務(wù)handler處理
public class server {
public static void main(String[] args) throws Exception {
//1 用于接受客戶端連接的線程工作組
EventLoopGroup boss = new NioEventLoopGroup();
//ONE:
//2 用于對(duì)接受客戶端連接讀寫(xiě)操作的線程工作組
EventLoopGroup work = new NioEventLoopGroup();
//TWO:
//3 輔助類(lèi)。用于幫助我們創(chuàng)建NETTY服務(wù)
ServerBootstrap b = new ServerBootstrap();
b.group(boss, work) //綁定兩個(gè)工作線程組
.channel(NioServerSocketChannel.class) //設(shè)置NIO的模式
.option(ChannelOption.SO_BACKLOG, 1024*2) //設(shè)置TCP緩沖區(qū)
//.option(ChannelOption.SO_SNDBUF, 32*1024) // 設(shè)置發(fā)送數(shù)據(jù)的緩存大小
.option(ChannelOption.SO_RCVBUF, 32 * 1024*2*2)
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE) // 設(shè)置保持連接
.childOption(ChannelOption.SO_SNDBUF, 32 * 1024)
// 初始化綁定服務(wù)通道
.childHandler(new initHandler());
//b.option("receiveBufferSizePredictorFactory", new FixedReceiveBufferSizePredictorFactory(65535));
ChannelFuture cf = b.bind(8765).sync();
//釋放連接
cf.channel().closeFuture().sync();
work.shutdownGracefully();
boss.shutdownGracefully();
}
}
ChannelInitializer
public class initHandler extends ChannelInitializer<SocketChannel> {
final AcceptorIdleStateTrigger idleStateTrigger=new AcceptorIdleStateTrigger();
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new StringEncoder());
pipeline.addLast(new ByteArrayEncoder());
//自定義StringDecoder淮腾,其余的都是用的netty提供的
pipeline.addLast(new StringDecoder1());
pipeline.addLast(new byteArrayDecoder1());
pipeline.addLast(new ServerHandler());
pipeline.addLast(new ServerHandler2());
}
}
自定義StringDecoder
public class StringDecoder1 extends ByteToMessageDecoder {
private static final Logger logger = LoggerFactory.getLogger(StringDecoder1.class);
final int length = 2048;
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {
try {
in.retain();
byte[] headArray = new byte[3];
in.readBytes(headArray);
String head = new String(headArray);
// 把讀取的起始位置重置
in.resetReaderIndex();
if (TcnConstant.CMD_HEADER.equals(head)) {
int strBeginIndex = in.readerIndex();
int readableBytes = in.readableBytes();
byte[] tailArray = new byte[3];
//數(shù)據(jù)末尾
in.getBytes(readableBytes-3,tailArray);
String tail = new String(tailArray);
in.resetReaderIndex();
//沒(méi)接收完
if (!TcnConstant.CMD_TAIL.equals(tail)) {
logger.info("可讀字節(jié)數(shù)readableBytes is {}",readableBytes);
in.readerIndex(strBeginIndex);
return;
}
ByteBufToBytes reader = new ByteBufToBytes();
String msg = new String(reader.read(in));
//in.retain(1);
list.add(msg);d(in));
list.add(msg);
} else {
channelHandlerContext.fireChannelRead(in);
}
}catch (Exception e){
logger.info("=================");
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.err.println("--------數(shù)據(jù)讀異常----------: ");
cause.printStackTrace();
ctx.close();
}
}
自定義byte解碼器1
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.bangmart.android.util.DataUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Created by zhangkai on 2018/7/20.
*/
public class byteArrayDecoder1 extends ByteToMessageDecoder {
private static final Logger logger = LoggerFactory.getLogger(byteArrayDecoder1.class);
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {
try {
in.retain();
byte[] headArray = new byte[2];
in.readBytes(headArray);
in.resetReaderIndex();
FactoryEnum fe = FactoryUtil.indentify(headArray,channelHandlerContext.channel());
if(null!=fe && FactoryEnum.XY.getName().equals(fe.getName())) {
int readableBytes = in.readableBytes();
int beginIndex = in.readerIndex();
boolean heart = false;
ByteBufToBytes reader = new ByteBufToBytes();
byte[] data = reader.read(in);
String hexStr = DataUtil.ByteArrToHexString(data);
String[] hexArray = hexStr.split(" ");
if (hexArray.length < XyConstant.headLength) {
if (hexArray.length == XyConstant.HEARTBEAT_MSG_FIELD.HEARTBEAT_MSG_LENGTH && hexArray[0].equalsIgnoreCase(XyConstant.heartHead)) {
//是心跳
heart = true;
} else {
return;
}
}
if (!heart) {
int dataLength = DataUtil.HexToInt(hexArray[XyConstant.msgLengthIndex]);
if (readableBytes < dataLength) {
logger.info("可讀字節(jié)數(shù)小于數(shù)據(jù)長(zhǎng)度");
in.readerIndex(beginIndex);
return;
}
//粘包
if (readableBytes > dataLength) {
data = Arrays.copyOf(data, dataLength);
}
}
/**
* 1.每一個(gè)bytebuf都有一個(gè)計(jì)數(shù)器糟需,每次調(diào)用計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器為0時(shí)則不可用谷朝。
* 2.如果當(dāng)前bytebuf中數(shù)據(jù)包含多條消息洲押,本條信息會(huì)通過(guò)list返回被繼續(xù)封裝成一個(gè)新的byte[]返回下一個(gè)hander處理
* 3.retain方法是將當(dāng)前的bytebuf計(jì)數(shù)器加1
* 4.如果不這樣做,會(huì)報(bào)異常 io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
* */
list.add(data);}
else {
//這里是為了讓各自的消息一定能到各自的decoder中處理圆凰,不然會(huì)發(fā)生多次讀半包異常
//比如說(shuō)杈帐,如果一個(gè)消息因?yàn)榫W(wǎng)絡(luò)的原因,netty需要多次(大于2)讀取才能讀完,那么就一定需要確保各自的消息
//在各自的decoder中才能正確讀取到挑童。
//第一次和第二次讀取時(shí)累铅,都是拼接好的全部數(shù)據(jù),當(dāng)?shù)谌巫x取時(shí)站叼,就是單個(gè)數(shù)據(jù)
if(null == fe){
if(null == fe){
byte[] msg = new byte[in.readableBytes()];
in.readBytes(msg);
logger.error("解析消息失敗娃兽,未識(shí)別消息所屬?gòu)S家{}",DataUtil.bytesToHexString(msg));
return;
}
channelHandlerContext.fireChannelRead(in);
}
}catch (Exception e){
logger.info("decoder異常================={}",e);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.err.println("--------數(shù)據(jù)讀異常----------: ");
cause.printStackTrace();
ctx.close();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
System.err.println("--------數(shù)據(jù)讀取完畢----------");
}
}
自定義byte解碼器2
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.bangmart.android.util.DataUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Created by zhangkai on 2018/7/20.
*/
public class byteArrayDecoder2 extends ByteToMessageDecoder {
private static final Logger logger = LoggerFactory.getLogger(byteArrayDecoder1.class);
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {
try {
ByteBufToBytes reader = new ByteBufToBytes();
in.retain();
int readableBytes = in.readableBytes();
int beginIndex = in.readerIndex();
byte[] data = reader.read(in);
int frameLenHigh=data[2]& FactoryConstant.BYTE_MASK;
int frameLenLow=data[3]&FactoryConstant.BYTE_MASK;
int frameLen=frameLenHigh* (FactoryConstant. HEXADECIMAL* FactoryConstant.HEXADECIMAL)+frameLenLow;
int dataLength = frameLen;
if (readableBytes < dataLength) {
logger.info("可讀字節(jié)數(shù){}===小于===數(shù)據(jù)長(zhǎng)度{}",readableBytes,dataLength);
// in.resetReaderIndex();
in.readerIndex(beginIndex);
return;
}
//粘包
if (readableBytes > dataLength) {
logger.info("可讀字節(jié)數(shù){}===大于===數(shù)據(jù)長(zhǎng)度{}",readableBytes,dataLength);
data = Arrays.copyOf(data, dataLength);
}
/**
* 1.每一個(gè)bytebuf都有一個(gè)計(jì)數(shù)器,每次調(diào)用計(jì)數(shù)器減1大年,當(dāng)計(jì)數(shù)器為0時(shí)則不可用换薄。
* 2.如果當(dāng)前bytebuf中數(shù)據(jù)包含多條消息,本條信息會(huì)通過(guò)list返回被繼續(xù)封裝成一個(gè)新的byte[]返回下一個(gè)hander處理
* 3.retain方法是將當(dāng)前的bytebuf計(jì)數(shù)器加1
* 4.如果不這樣做翔试,會(huì)報(bào)異常 io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
* */
list.add(data);
}catch (Exception e){
logger.info("decoder異常================={}",e);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.err.println("--------數(shù)據(jù)讀異常----------: ");
cause.printStackTrace();
ctx.close();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
System.err.println("--------數(shù)據(jù)讀取完畢----------");
}
}
業(yè)務(wù)處理Handler
/**
* Created by zhangkai on 2018/6/11.
*/
public class ServerHandler extends ChannelInboundHandlerAdapter{
/**
* 當(dāng)我們通道進(jìn)行激活的時(shí)候 觸發(fā)的監(jiān)聽(tīng)方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.err.println("--------通道激活------------");
}
/**
* 當(dāng)我們的通道里有數(shù)據(jù)進(jìn)行讀取的時(shí)候 觸發(fā)的監(jiān)聽(tīng)方法
*/
@Override
public void channelRead(ChannelHandlerContext ctx /*NETTY服務(wù)上下文*/, Object msg /*實(shí)際的傳輸數(shù)據(jù)*/) throws Exception {
if(msg instanceof String) {
System.out.println("----------XXX-----"+(String) msg);
ctx.writeAndFlush("我是XXX服務(wù)端");
}else {
ctx.fireChannelRead(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.err.println("--------數(shù)據(jù)讀取完畢----------");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.err.println("--------數(shù)據(jù)讀異常----------: ");
cause.printStackTrace();
ctx.close();
}
}
/**
* Created by zhangkai on 2018/6/11.
*/
public class ServerHandler2 extends ChannelInboundHandlerAdapter{
/**
* 當(dāng)我們通道進(jìn)行激活的時(shí)候 觸發(fā)的監(jiān)聽(tīng)方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.err.println("--------通道激活------------");
}
/**
* 當(dāng)我們的通道里有數(shù)據(jù)進(jìn)行讀取的時(shí)候 觸發(fā)的監(jiān)聽(tīng)方法
*/
@Override
public void channelRead(ChannelHandlerContext ctx /*NETTY服務(wù)上下文*/, Object msg /*實(shí)際的傳輸數(shù)據(jù)*/) throws Exception {
if(msg instanceof byte[]){
String hexStr = DataUtil.ByteArrToHexString((byte[]) msg);
System.out.println("----XXX------"+hexStr);
ctx.writeAndFlush(DataUtil.hexStringToBytes("1D011E"));
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.err.println("--------數(shù)據(jù)讀取完畢----------");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.err.println("--------數(shù)據(jù)讀異常----------: ");
cause.printStackTrace();
ctx.close();
}
}
Client測(cè)試,模擬兩個(gè)發(fā)送不同協(xié)議數(shù)據(jù)的客戶端
/**
* Created by zhangkai on 2018/6/11.
*/
public class client {
public static void main(String[] args) throws Exception {
//ONE:
//1 線程工作組
EventLoopGroup work = new NioEventLoopGroup();
//TWO:
//3 輔助類(lèi)轻要。用于幫助我們創(chuàng)建NETTY服務(wù)
Bootstrap b = new Bootstrap();
b.group(work) //綁定工作線程組
.channel(NioSocketChannel.class) //設(shè)置NIO的模式
// 初始化綁定服務(wù)通道
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
// 為通道進(jìn)行初始化: 數(shù)據(jù)傳輸過(guò)來(lái)的時(shí)候會(huì)進(jìn)行攔截和執(zhí)行
sc.pipeline().addLast( new StringEncoder());
sc.pipeline().addLast( new StringDecoder());
sc.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture cf = b.connect("0.0.0.0", 8765).syncUninterruptibly();
// String str = "###3000${\"Mid\":\"1711290001\",“TimeSp”: “1511936017”}$AABBCCDD&&&";
String str = "###5000${\"Mid\":\"7105000011\",\"MaxSlot\":\"60\",\"SlotInfo\":[{\"SlotNum\":\"1\",\"Status\":\"1\"},{\"SlotNum\":\"2\",\"Status\":\"1\"},\n" +
"{\"SlotNum\":\"3\",\"Status\":\"1\"},{\"SlotNum\":\"4\",\"Status\":\"1\"},{\"SlotNum\":\"5\",\"Status\":\"1\"},{\"SlotNum\":\"11\",\"Status\":\"1\"},{\"SlotNum\":\"12\",\"Status\":\"1\"},{\"SlotNum\":\"13\",\"Status\":\"1\"},{\"SlotNum\":\"14\",\"Status\":\"1\"},{\"SlotNum\":\"15\",\"Status\":\"1\"},{\"SlotNum\":\"21\",\"Status\":\"1\"},{\"SlotNum\":\"22\",\"Status\":\"1\"},{\"SlotNum\":\"23\",\"Status\":\"1\"},{\"SlotNum\":\"24\",\"Status\":\"1\"},{\"SlotNum\":\"25\",\"Status\":\"1\"},{\"SlotNum\":\"26\",\"Status\":\"1\"},{\"SlotNum\":\"27\",\"Status\":\"1\"},{\"SlotNum\":\"28\",\"Status\":\"1\"},{\"SlotNum\":\"29\",\"Status\":\"1\"},{\"SlotNum\":\"30\",\"Status\":\"1\"},{\"SlotNum\":\"31\",\"Status\":\"1\"},{\"SlotNum\":\"32\",\"Status\":\"1\"},{\"SlotNum\":\"33\",\"Status\":\"1\"},{\"SlotNum\":\"34\",\"Status\":\"1\"},{\"SlotNum\":\"35\",\"Status\":\"1\"},{\"SlotNum\":\"36\",\"Status\":\"1\"},{\"SlotNum\":\"37\",\"Status\":\"1\"},{\"SlotNum\":\"38\",\"Status\":\"1\"},{\"SlotNum\":\"39\",\"Status\":\"1\"},{\"SlotNum\":\"40\",\"Status\":\"1\"},{\"SlotNum\":\"41\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"43\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},\n" +
"{\"SlotNum\":\"3\",\"Status\":\"1\"},{\"SlotNum\":\"4\",\"Status\":\"1\"},{\"SlotNum\":\"5\",\"Status\":\"1\"},{\"SlotNum\":\"11\",\"Status\":\"1\"},{\"SlotNum\":\"12\",\"Status\":\"1\"},{\"SlotNum\":\"13\",\"Status\":\"1\"},{\"SlotNum\":\"14\",\"Status\":\"1\"},{\"SlotNum\":\"15\",\"Status\":\"1\"},{\"SlotNum\":\"21\",\"Status\":\"1\"},{\"SlotNum\":\"22\",\"Status\":\"1\"},{\"SlotNum\":\"23\",\"Status\":\"1\"},{\"SlotNum\":\"24\",\"Status\":\"1\"},{\"SlotNum\":\"25\",\"Status\":\"1\"},{\"SlotNum\":\"26\",\"Status\":\"1\"},{\"SlotNum\":\"27\",\"Status\":\"1\"},{\"SlotNum\":\"28\",\"Status\":\"1\"},{\"SlotNum\":\"29\",\"Status\":\"1\"},{\"SlotNum\":\"30\",\"Status\":\"1\"},{\"SlotNum\":\"31\",\"Status\":\"1\"},{\"SlotNum\":\"32\",\"Status\":\"1\"},{\"SlotNum\":\"33\",\"Status\":\"1\"},{\"SlotNum\":\"34\",\"Status\":\"1\"},{\"SlotNum\":\"35\",\"Status\":\"1\"},{\"SlotNum\":\"36\",\"Status\":\"1\"},{\"SlotNum\":\"37\",\"Status\":\"1\"},{\"SlotNum\":\"38\",\"Status\":\"1\"},{\"SlotNum\":\"39\",\"Status\":\"1\"},{\"SlotNum\":\"40\",\"Status\":\"1\"},{\"SlotNum\":\"41\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"43\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},\n" +
"{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"},{\"SlotNum\":\"42\",\"Status\":\"1\"}]}$AABBCCDD&&&";
System.out.println(str.length());
cf.channel().writeAndFlush(str);
//釋放連接
cf.channel().closeFuture().sync();
work.shutdownGracefully();
}
}
/**
* Created by zhangkai on 2018/6/11.
*/
public class client2 {
public static void main(String[] args) throws Exception {
//ONE:
//1 線程工作組
EventLoopGroup work = new NioEventLoopGroup();
//TWO:
//3 輔助類(lèi)。用于幫助我們創(chuàng)建NETTY服務(wù)
Bootstrap b = new Bootstrap();
b.group(work) //綁定工作線程組
.channel(NioSocketChannel.class) //設(shè)置NIO的模式
// 初始化綁定服務(wù)通道
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast( new ByteArrayEncoder());
sc.pipeline().addLast(new ByteArrayDecoder());
sc.pipeline().addLast(new ClientHandler2());
}
});
ChannelFuture cf = b.connect("0.0.0.0", 8765).syncUninterruptibly();
// 0xFF 0x01 OX00
byte[] bytes = DataUtil.hexStringToBytes("FF0100");
cf.channel().writeAndFlush(bytes);
//釋放連接
cf.channel().closeFuture().sync();
work.shutdownGracefully();
}
}
用到的工具類(lèi)
/**
*數(shù)據(jù)轉(zhuǎn)換工具
*/
public class DataUtil {
//-------------------------------------------------------
// 判斷奇數(shù)或偶數(shù)垦缅,位運(yùn)算冲泥,最后一位是1則為奇數(shù),為0是偶數(shù)
static public int isOdd(int num)
{
return num & 0x1;
}
//-------------------------------------------------------
static public int HexToInt(String inHex)//Hex字符串轉(zhuǎn)int
{
return Integer.parseInt(inHex, 16);
}
//-------------------------------------------------------
static public byte HexToByte(String inHex)//Hex字符串轉(zhuǎn)byte
{
return (byte)Integer.parseInt(inHex,16);
}
//-------------------------------------------------------
static public String Byte2Hex(Byte inByte)//1字節(jié)轉(zhuǎn)2個(gè)Hex字符
{
return String.format("%02x", inByte).toUpperCase();
}
//-------------------------------------------------------
static public String ByteArrToHexString(byte[] inBytArr)//字節(jié)數(shù)組轉(zhuǎn)hex字符串
{
StringBuilder strBuilder=new StringBuilder();
int j=inBytArr.length;
for (int i = 0; i < j; i++)
{
strBuilder.append(Byte2Hex(inBytArr[i]));
strBuilder.append(" ");
}
return strBuilder.toString();
}
//-------------------------------------------------------
static public String ByteArrToHexString(byte[] inBytArr, int offset, int byteCount)//字節(jié)數(shù)組轉(zhuǎn)轉(zhuǎn)hex字符串壁涎,可選長(zhǎng)度
{
StringBuilder strBuilder=new StringBuilder();
int j=byteCount;
for (int i = offset; i < j; i++)
{
strBuilder.append(Byte2Hex(inBytArr[i]));
strBuilder.append(" ");
}
return strBuilder.toString();
}
//-------------------------------------------------------
//hex字符串轉(zhuǎn)字節(jié)數(shù)組
static public byte[] HexToByteArr(String inHex)//hex字符串轉(zhuǎn)字節(jié)數(shù)組
{
int hexlen = inHex.length();
byte[] result;
if (isOdd(hexlen)==1)
{//奇數(shù)
hexlen++;
result = new byte[(hexlen/2)];
inHex="0"+inHex;
}else {//偶數(shù)
result = new byte[(hexlen/2)];
}
int j=0;
for (int i = 0; i < hexlen; i+=2)
{
result[j]=HexToByte(inHex.substring(i,i+2));
j++;
}
return result;
}
/**
* 獲取無(wú)空格的hexString
* @param str
* @return
*/
static public String getFomattedHexString(String str){
StringBuilder sb = new StringBuilder();
String[] strArr = str.split(" ");
int len = strArr.length;
for (int i = 0; i < len; i++) {
sb.append(strArr[i]);
}
return sb.toString();
}
/**********************************************************************/
/* Convert byte[] to hex string.這里我們可以將byte轉(zhuǎn)換成int凡恍,然后利用Integer.toHexString(int)來(lái)轉(zhuǎn)換成16進(jìn)制字符串。
* @param src byte[] data
* @return hex string
*/
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* Convert hex string to byte[]
* @param hexString the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* Convert char to byte
* @param c char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
public static String getCheckXOR(String data) {
byte[] bytes = DataUtil.HexToByteArr(data);
byte bcc = caluBCC(bytes, 0, bytes.length);
return Byte2Hex(bcc);
}
/*****************************數(shù)據(jù)的bcc校驗(yàn)**************************/
public static byte caluBCC(byte[] pByte, int start, int length) {
if(pByte == null || pByte.length <= 0 || length <= 0 || start < 0){
return -1;
}
byte checkSum = 0;
for (int i = start; i < start+length; i++) {
checkSum ^= pByte[i];
}
return checkSum;
}
/**
* int型轉(zhuǎn)化為byte數(shù)組
* @param i
* @return
*/
public static byte[] intToByteArray1(int i) {
byte[] result = new byte[4];
result[0] = (byte)((i >> 24) & 0xFF);
result[1] = (byte)((i >> 16) & 0xFF);
result[2] = (byte)((i >> 8) & 0xFF);
result[3] = (byte)(i & 0xFF);
return result;
}
}
下面是整個(gè)過(guò)程中的數(shù)據(jù)流向
注:在服務(wù)端檢測(cè)到客戶端斷線后怔球,服務(wù)端主動(dòng)關(guān)閉連接嚼酝,這時(shí)候會(huì)報(bào)這個(gè)錯(cuò)誤,
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:101) ~[netty-all-4.0.28.Final.jar:4.0.28.Final]竟坛;
解決辦法:
1闽巩、添加ByteBuf.retain();生產(chǎn)上handler繼承的是SimpleChannelInboundHandler,解決辦法是下面這樣處理