背景:
需要实现海康摄像机外网访问,由于摄像机没有公网IP(这个可以通过端口映射解决),且所在网络不是专线,公网IP会变动。通过DDNS动态域名解析或者使用萤石云、easynvr等云平台也可以解决,但是这些平台都需要付费,切费用较高,于是我就想通过解析协议包的方式来获取摄像机IP并进行更新。
实现步骤:
**一、配置FTP协议。**进入摄像机后台系统,浏览器输入摄像机IP即可,用户名密码默认是admin和a12345678,登录成功后点击“配置”–“网络”–“高级配置”–“FTP”,进行ftp配置,这是摄像机抓拍人脸后向服务器发送图片的协议,若服务器没有搭建FTP服务,可自行百度搭建。
二、捕获协议包。 在ftp服务所在的服务器上通过tcpdump执行sudo tcpdump -i 网卡号 host 网卡号对应的ip地址 and port ftp服务端口号 -w 协议包保存文件.pcap
命令抓取ftp服务端口上的协议包并保存,若没有安装tcpdump可百度安装。
三、创建device(摄像机)数据库。
CREATE TABLE `t_device` (`id` int(10) NOT NULL AUTO_INCREMENT,`devicecode` varchar(200) DEFAULT NULL COMMENT '设备编码',`devicename` varchar(50) DEFAULT NULL COMMENT '设备监控名称',`deviceip` varchar(50) DEFAULT NULL COMMENT '设备ip',`deviceport` varchar(50) DEFAULT NULL COMMENT '设备端口号',`username` varchar(50) DEFAULT NULL COMMENT '设备用户名',`password` varchar(50) DEFAULT NULL COMMENT '设备登录密码',`devicetype` int(10) DEFAULT '1' COMMENT '设备类型(1:摄像机,2:NVR)',`channel` varchar(50) DEFAULT NULL COMMENT 'NVR存通道数,其余存通道id',`vendor` varchar(50) DEFAULT NULL COMMENT '生产厂商(海康:HK,大华:DS)',`position` varchar(50) DEFAULT NULL COMMENT '安装位置',`disknumber` varchar(50) DEFAULT NULL COMMENT '硬盘序列号',`diskchannel` varchar(200) DEFAULT NULL COMMENT '监控点编码',`type` varchar(10) DEFAULT '2' COMMENT '设备状态;1、在线,2、离线',`machinename` varchar(50) DEFAULT NULL COMMENT '门口机名称',`code` varchar(50) DEFAULT NULL COMMENT '单元编号',`uuid` varchar(50) DEFAULT NULL COMMENT '唯一编码',`creattime` timestamp NULL DEFAULT NULL COMMENT '创建时间',`isdelete` int(10) DEFAULT '0' COMMENT '0、未删除,1、已删除',`lon` varchar(50) DEFAULT NULL COMMENT '经度',`lat` varchar(50) DEFAULT NULL COMMENT '纬度',`boxcode` varchar(100) DEFAULT NULL COMMENT '盒子编码',`platformtype` int(1) DEFAULT NULL COMMENT '平台类型:1、感知,2、海康',`publicip` varchar(50) DEFAULT NULL COMMENT '公网ip',`publicport` varchar(50) DEFAULT NULL COMMENT '公网ip端口号',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='摄像机管理表';
四、创建maven工程。
1.引入主要依赖并编写application.yml配置文件和application启动类。
<dependency><groupId>io.pkts</groupId><artifactId>pkts-streams</artifactId><version>3.0.3</version></dependency><dependency><groupId>io.pkts</groupId><artifactId>pkts-core</artifactId><version>3.0.3</version></dependency>
2.编写主要代码,其中device为你数据库中摄像头所对应的pojo
import com.pojo.Device;
import com.service.DeviceService;
import com.zxm.utils.LogUtils;
import io.pkts.Pcap;
import io.pkts.buffer.Buffer;
import io.pkts.packet.IPPacket;
import io.pkts.packet.Packet;
import io.pkts.protocol.Protocol;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.util.List;
/*** @Description TODO* @Author silence.Z* @Date 2020/10/20 15:00* @Version 1.0**/
@Component
@Order(value = 1)
public class DeviceRunner implements ApplicationRunner {
@Autowiredprivate DeviceService deviceService;private static String logPath = "/home/ganinfo/logs/pacp.log";//private static String logPath = "D:\\pacp.log";@Overridepublic void run(ApplicationArguments args) throws Exception {
Pcap pcap = null;try {
//读取解析pcap文件//pcap = Pcap.openStream("d:\\dump3615.pcap");pcap = Pcap.openStream("/home/ganinfo/dump3615.pcap");while (true) {
try {
pcap.loop((final Packet packet) -> {
if (packet.hasProtocol(Protocol.TCP)) {
//获取pcap文件中的tcp协议包Buffer payload = packet.getPacket(Protocol.TCP).getPayload();//根据协议包获取IPV4地址IPPacket ip = (IPPacket) packet.getPacket(Protocol.IPv4);String sourceIP = ip.getSourceIP();//获取tcp协议包中的摄像机向ftp服务器发送的图片名称并对其进行截取,获得设备编号if (payload != null && payload.toString().contains("STOR")) {
String devicecode = payload.readLine().toString().substring(5, 13);LogUtils.log(logPath, sourceIP + "," + devicecode);//查询数据库是否存在该设备号的摄像机List<Device> devices = deviceService.selectByCode(devicecode);Device device = devices.get(0);String publicip = device.getPublicip();//如果数据库存在唯一摄像机且数据库中获取到的公网IP与根据协议包获取到的IPV4地址不相同if (devices.size() == 1 && !device.getPublicip().equals(sourceIP)){
//更新数据库,将数据库中所有公网IP相同的数据进行更新device.setPublicip(sourceIP);int i = deviceService.updateDevice(device,publicip);if (i > 0){
LogUtils.log(logPath,"摄像机"+devicecode+"公网IP发生变动,数据库中所有与其公网IP相同的设备更新成功! 原IP为:" + publicip + ",现IP为:" + sourceIP + ",更新的设备数量为:" + i);}else {
LogUtils.log(logPath,"!!!!!!!!!!!摄像机"+devicecode+"公网IP发生变动,更新失败! 原IP为:" + publicip + ",现IP为:" + sourceIP + ",更新的设备数量为:" + i);}}System.out.println(sourceIP + "," + devicecode);}}return true;});//每隔5秒解析一次协议包,之后建议加长Thread.sleep(1000 * 5);} catch (Exception e) {
e.printStackTrace();}}} catch (Exception e) {
e.printStackTrace();}}
}
3.为了查看日志方便,编写了LogUtils工具类。
public class LogUtils {
public static void log(String logPath, String msg) {
OutputStream out = null;OutputStreamWriter ow = null;BufferedWriter bw = null;SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String time = df.format(new Date());msg = time + " " + msg;File newFile = null;try {
newFile = new File(logPath);if (!newFile.exists()) {
newFile.createNewFile();}out = new BufferedOutputStream(new FileOutputStream(newFile, true));ow = new OutputStreamWriter(out, "utf-8");bw = new BufferedWriter(ow);bw.write(msg);bw.newLine();bw.flush();} catch (IOException var9) {
var9.printStackTrace();}}
}
五、打包发布到服务器。
1.点击idea下方的“Terminal”再通过mvn clean install
命令进行打包。
2.上传到服务器,通过java -jar jar包名
命令进行运行,查看logPath路径下的日志,成功。
若需要完整代码或有更好的解决方案,欢迎评论区留言!