摄像机、NVR公网IP不固定解决方案(Java+tcpdump)

236 阅读4分钟

我正在参加「掘金·启航计划」

背景:

需要实现海康摄像机外网访问,由于摄像机没有公网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 {
    @Autowired
    private DeviceService deviceService;

    private static String logPath = "/home/ganinfo/logs/pacp.log";
    //private static String logPath = "D:\\pacp.log";

    @Override
    public 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路径下的日志,成功。 在这里插入图片描述 若需要完整代码或有更好的解决方案,欢迎评论区留言!