liusuyi
2026-05-06 99ab0d46d30b095d0e1c9d31349f42e0cefc9881
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.ard.gb28181.transmit.event.request.impl.message.notify.cmd;
 
import com.ard.gb28181.api.common.RemoteAddressInfo;
import com.ard.gb28181.config.UserSetting;
import com.ard.gb28181.api.domain.Device;
import com.ard.gb28181.api.domain.SipMsgInfo;
import com.ard.gb28181.service.IDeviceService;
import com.ard.gb28181.task.deviceStatus.DeviceStatusTaskRunner;
import com.ard.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.ard.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.ard.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
import com.ard.gb28181.api.utils.DateUtil;
import com.ard.gb28181.utils.IpPortUtil;
import com.ard.gb28181.api.utils.SipUtils;
import gov.nist.javax.sip.message.SIPRequest;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
 
/**
 * 状态信息(心跳)报送
 */
@Slf4j
@Component
public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
 
 
    private final static String cmdType = "Keepalive";
 
    private final ConcurrentLinkedQueue<SipMsgInfo> taskQueue = new ConcurrentLinkedQueue<>();
 
    @Autowired
    private NotifyMessageHandler notifyMessageHandler;
 
    @Autowired
    private IDeviceService deviceService;
 
    @Autowired
    private DeviceStatusTaskRunner statusTaskRunner;
 
    @Autowired
    private UserSetting userSetting;
 
    @Override
    public void afterPropertiesSet() throws Exception {
        notifyMessageHandler.addHandler(cmdType, this);
    }
 
    @Override
    public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
        if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) {
            log.error("[心跳] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue());
            return;
        }
        taskQueue.offer(new SipMsgInfo(evt, device, rootElement));
    }
 
    @Scheduled(fixedDelay = 100)
    public void executeTaskQueue() {
        if (taskQueue.isEmpty()) {
            return;
        }
        List<SipMsgInfo> handlerCatchDataList = new ArrayList<>();
        int size = taskQueue.size();
        for (int i = 0; i < size; i++) {
            SipMsgInfo poll = taskQueue.poll();
            if (poll != null) {
                handlerCatchDataList.add(poll);
            }
        }
        if (handlerCatchDataList.isEmpty()) {
            return;
        }
        List<Device> deviceListForUpdate = new ArrayList<>();
        for (SipMsgInfo sipMsgInfo : handlerCatchDataList) {
            if (sipMsgInfo == null) {
                continue;
            }
            RequestEvent evt = sipMsgInfo.getEvt();
            // 回复200 OK
            try {
                responseAck((SIPRequest) evt.getRequest(), Response.OK);
            } catch (SipException | InvalidArgumentException | ParseException e) {
                log.error("[命令发送失败] 心跳回复: {}", e.getMessage());
            }
            Device device = sipMsgInfo.getDevice();
            SIPRequest request = (SIPRequest) evt.getRequest();
 
            RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
            if (device.getIp() == null || !device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
                log.info("[收到心跳] 地址变化, {}({}), {}:{}->{}", device.getName(), device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort(), request.getLocalAddress().getHostAddress());
                device.setPort(remoteAddressInfo.getPort());
                device.setHostAddress(IpPortUtil.concatenateIpAndPort(remoteAddressInfo.getIp(), String.valueOf(remoteAddressInfo.getPort())));
                device.setIp(remoteAddressInfo.getIp());
                device.setLocalIp(request.getLocalAddress().getHostAddress());
            }
 
            device.setKeepaliveTime(DateUtil.getNow());
 
            if (device.isOnLine()) {
                deviceListForUpdate.add(device);
                long expiresTime = Math.min(device.getExpires(), device.getHeartBeatInterval() * device.getHeartBeatCount()) * 1000L;
                if (statusTaskRunner.containsKey(device.getDeviceId())) {
                    statusTaskRunner.updateDelay(device.getDeviceId(), expiresTime + System.currentTimeMillis());
                }
            } else {
                if (userSetting.getGbDeviceOnline() == 1) {
                    // 对于已经离线的设备判断他的注册是否已经过期
                    deviceService.online(device, null);
                }
            }
        }
        if (!deviceListForUpdate.isEmpty()) {
            deviceService.updateDeviceList(deviceListForUpdate);
        }
    }
}