Tcp服务端粘包处理,java代码解析视频流(含丢包的预防处理)


视频流的传输采用的GB28181国标协议。视频格式RTP+ts,对接的宇视。

    tcp数据流格式:magicNumber(2字节)+Datalength(2字节)+dataContent


代码实例功能:粘包,丢包处理(因为双方网络环境设计到中间防火墙等,网络状况比较复杂),监测断流自动重启(ipc前端中        断,后端依据前端异常优化处理)。








public class TcpRtpClient extends RtpClient {
private static final Logger LOG = LoggerFactory.getLogger(TcpRtpClient.class);
private ServerSocket ss;
private BufferedInputStream dis;
private byte[] buffer;
private Thread rtpWorkThread;
private RtpTask rtpTask;
private TaskContext context;
private int len = 0;
// 参数数据变量
byte[] save;


public TcpRtpClient(RtpClient.PacketProcessor processor, TaskContext context) throws Exception {
packetProcessor = processor;
this.context = context;
buffer = new byte[context.getRtpBufferSize()];
ip = Utils.getHostIp();
resetSocket();
rtpTask = new RtpTask();
rtpWorkThread = new Thread(rtpTask, "rtp-listen-thread");
}


@Override
public void start() {
if (rtpWorkThread != null) {
rtpWorkThread.start();
}
}


@Override
public void stop() {
if (rtpTask != null) {
rtpTask.stop();
if (ss != null) {
try {
if (dis!=null) {
dis.close();
}
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
if (rtpWorkThread != null) {
rtpWorkThread.interrupt();
rtpWorkThread.join();
}
} catch (InterruptedException e) {
} catch (Exception e) {
}
}


@Override
public void restart() throws Exception {
stop();


resetSocket();
rtpTask = new RtpTask();
rtpWorkThread = new Thread(rtpTask, "rtp-listen-thread-" + context.getTaskId());
start();
}


@Override
public boolean isStopped() {
if (rtpWorkThread != null) {
return !rtpWorkThread.isAlive();
}
return true;
}


private void resetSocket() throws Exception {
RetryUtil.retry(() -> {
port = RandomUtils.nextInt(1000, 65536);
InetAddress address = InetAddress.getByName(ip);
ss = new ServerSocket(port, 1, address);
ss.setSoTimeout(context.getListenTimeout());
return true;
}, 100, 100L, true);
}


private class RtpTask implements Runnable {
private volatile boolean stop = false;


@Override
public void run() {
try {
if (dis == null) {
dis = new BufferedInputStream(ss.accept().getInputStream());
}
while (!stop) {
try {
if ((len = dis.read(buffer)) > -1) {
buffer = Arrays.copyOfRange(buffer, 0, len);
getData(buffer, len, packetProcessor);
}
} catch (SocketTimeoutException e) {
if (!stop) {
LOG.error("topic :{} , happen SocketTimeoutException :{}",context.getTopic(),e.getMessage());
packetProcessor.OnException(e);
}

} catch (Exception e) {

    

     Thread.sleep(6000L);
packetProcessor.OnException(e);
}
}
} catch (Exception e) {
if (!stop) {
packetProcessor.OnException(e);
}
}
}


public void stop() {
stop = true;
}


// -------------------------------------------------------
public int getLen(byte[] arr, int index) {
String l1 = length(arr[index]);
String l2 = length(arr[index + 1]);
return Integer.valueOf(l1 + l2, 2);
}


public String length(byte b) {
String value = Integer.toBinaryString(b & 0xff);
int len = value.length();
StringBuilder s = new StringBuilder(value);
if (len < 8) {
int i = 8 - len;
for (int j = 0; j < i; j++) {
s.insert(0, "0");
}
}
return s.toString();


}


public int findLenIndex(byte[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (Integer.toHexString(arr[i] & 0xff).equals("24")
&& Integer.toHexString(arr[i + 1] & 0xff).equals("0")) {
return i + 2;
}
continue;
}
return -1;
}


public byte[] getResidualData(byte[] buffer, int packageSize) {
return Arrays.copyOfRange(buffer, packageSize, buffer.length);
}


public byte[] getRtpData(byte[] buffer, int index, int dataCount) {
return Arrays.copyOfRange(buffer, index + 2, index + 2 + dataCount);
}


public void getData(byte[] buffer, int len, PacketProcessor packetProcessor) throws Exception {
// 1.判断总的数据包字节Len是否超过4个字节ZX
// LOG.info("Topic : {} ,buffer length : {} ,readLength : {} , content is {}
// ",context.getTopic(),buffer.length , len ,Arrays.toString(buffer));
if (save == null) {
if (buffer.length > 4) {
// LOG.info("第一步 save == null magic number: {},{} readLength :
// {}",buffer[0],buffer[1] ,len);
int index = findLenIndex(buffer);// 找到24 00索引的后一个位置的下标
if (index == -1) {
len = 0;
// LOG.warn("Tcp happen lose packet,try to regroup data ! topic :
// {}",context.getTopic());
return;
}
// 05-03 : 防止索引越界,获取长度字节不完善
if (buffer.length < index + 2) {
save = Arrays.copyOfRange(buffer, 0, buffer.length);
return;
}


int dataCount = getLen(buffer, index);// 得到数据流的长度
int packageSize = dataCount + index + 2;


// 超过,进行解析,在判断包的总数据大小是否大于Len,大于取值,并且切割剩余数据,进行递归
if (buffer.length > packageSize) {
byte[] data = getRtpData(buffer, index, dataCount);
// datahub 存值


// LOG.info("the RTPData is : {},{},{},{},{} ,array length is
// :{}",buffer[index-2],buffer[index-1],buffer[index],buffer[index+1],Arrays.toString(data),data.length+4);
// 05-03: 防止取出数据异常情况,索引越界 将数据扔掉
if (data != null && data.length > 4) {
packetProcessor.OnPacket(data, data.length);
}
len = len - packageSize;
byte[] residualData = getResidualData(buffer, packageSize);
// 递归


// LOG.info("the residualData is : {} , this array length is : {}"
// ,Arrays.toString(residualData),residualData.length );
if (residualData[0] != 36 && residualData[1] != 0) {


// LOG.error("第二步 len > packageSize 进行数组切割 {},{}
// packageSize:{}",residualData[0],residualData[1],packageSize);
}
getData(residualData, len, packetProcessor);
} else if (buffer.length < packageSize) {
// 不超过,截取最多的数据,并且告知变量剩余数据大小在下个缓冲区内接着读取
save = Arrays.copyOfRange(buffer, 0, buffer.length);
// LOG.info("len < packageSize ,the save is : {} ",Arrays.toString(save));
if (save[0] != 36 && save[1] != 0) {
// LOG.info("第二步 len< packageSize 进行数组切割 存放save,准备与下个包拼接 magic 为 : {},{}
// ",save[0],save[1]);
}
} else {
byte[] data = getRtpData(buffer, index, dataCount);
// datahub 存值
packetProcessor.OnPacket(data, data.length);
len = 0;
}


} else {
save = Arrays.copyOfRange(buffer, 0, buffer.length);
// LOG.info(" len < 4 ,the save is : {} ",Arrays.toString(save));


}
} else {// save != null
// LOG.info("now save data is : {} ",Arrays.toString(save),"buffer data is
// :{}",Arrays.toString(buffer));
byte[] mergeData = ArrayUtils.addAll(save, buffer);
save = null;
len = 0;
// LOG.info("第1步 save!=null 进行数组拼装 拼装后magic 是: {},{} mergeData:
// {}",mergeData[0],mergeData[1],Arrays.toString(mergeData));
getData(mergeData, mergeData.length, packetProcessor);
}


}
}
}

猜你喜欢

转载自blog.csdn.net/pioneerx_x/article/details/80349051