diff --git a/common/pom.xml b/common/pom.xml index cb1d127..6e2abfb 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -44,6 +44,10 @@ com.fasterxml.jackson.core jackson-databind + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + com.fasterxml.jackson.core jackson-annotations diff --git a/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java b/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java index 3859a77..ff5e9f2 100644 --- a/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java +++ b/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java @@ -48,9 +48,9 @@ public class JsonUtils { } } - public static T parse(String json, TypeReference clazz) { + public static T parse(String xml, TypeReference clazz) { try { - return mapper.readValue(json, clazz); + return mapper.readValue(xml, clazz); } catch (Exception e) { e.printStackTrace(); return null; diff --git a/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java b/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java new file mode 100644 index 0000000..addd0b3 --- /dev/null +++ b/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java @@ -0,0 +1,111 @@ +package cn.skcks.docking.gb28181.common.xml; + +import com.fasterxml.jackson.core.json.JsonReadFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +import java.nio.charset.Charset; + +@SuppressWarnings({"unused"}) +public class XmlUtils { + private static final ObjectMapper mapper = new XmlMapper(); + static { + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + // 如果json中有新增的字段并且是实体类类中不存在的,不报错 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); + // 允许出现特殊字符和转义符 + mapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + // 允许出现单引号 + mapper.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true); + // 大驼峰 (首字母大写) + mapper.setPropertyNamingStrategy(new PropertyNamingStrategies.UpperCamelCaseStrategy()); + } + + public static String toXml(Object obj) { + try { + return mapper.writeValueAsString(obj); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static byte[] toByteXml(Object obj) { + try { + return mapper.writeValueAsString(obj).getBytes(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static byte[] toByteXml(Object obj, Charset charset) { + try { + return mapper.writeValueAsString(obj).getBytes(charset); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static byte[] toByteXml(Object obj, String charset) { + try { + return mapper.writeValueAsString(obj).getBytes(Charset.forName(charset)); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static T parse(byte[] xml, Class clazz) { + return parse(new String(xml), clazz); + } + + public static T parse(byte[] xml, Class clazz, Charset charset) { + return parse(new String(xml, charset), clazz); + } + + public static T parse(byte[] xml, Class clazz, String charset) { + return parse(new String(xml, Charset.forName(charset)), clazz); + } + + public static T parse(String xml, Class clazz) { + try { + return mapper.readValue(xml, clazz); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static T parse(byte[] xml, TypeReference clazz) { + return parse(new String(xml), clazz); + } + + public static T parse(byte[] xml, TypeReference clazz, Charset charset) { + return parse(new String(xml, charset), clazz); + } + + public static T parse(byte[] xml, TypeReference clazz, String charset) { + return parse(new String(xml, Charset.forName(charset)), clazz); + } + + public static T parse(String xml, TypeReference clazz) { + try { + return mapper.readValue(xml, clazz); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static T convert(Object object, Class clazz) { + return XmlUtils.parse(XmlUtils.toXml(object), clazz); + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/CmdType.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/CmdType.java new file mode 100644 index 0000000..68e09ea --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/CmdType.java @@ -0,0 +1,17 @@ +package cn.skcks.docking.gb28181.core.sip.gb28181.constant; + +@SuppressWarnings("unused") +public class CmdType { + public static final String KEEPALIVE = "Keepalive"; + public static final String DEVICE_CONFIG = "DeviceConfig"; + public static final String DEVICE_CONTROL = "DeviceControl"; + public static final String DEVICE_STATUS = "DeviceStatus"; + public static final String CATALOG = "Catalog"; + public static final String ALARM = "Alarm"; + public static final String MOBILE_POSITION = "MobilePosition"; + public static final String BROADCAST = "Broadcast"; + public static final String RECORD_INFO = "RecordInfo"; + public static final String MEDIA_STATUS = "MediaStatus"; + public static final String CONFIG_DOWNLOAD = "ConfigDownload"; + public static final String PRESET_QUERY = "PresetQuery"; +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/DefaultConstant.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/GB28181Constant.java similarity index 84% rename from gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/DefaultConstant.java rename to gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/GB28181Constant.java index 5c29765..6c20d75 100644 --- a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/DefaultConstant.java +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/constant/GB28181Constant.java @@ -1,6 +1,6 @@ package cn.skcks.docking.gb28181.core.sip.gb28181.constant; -public class DefaultConstant { +public class GB28181Constant { public static final String CHARSET = "GB2312"; public static final String GEO_COORD_SYS = "WGS84"; } diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/MessageProcessor.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/MessageProcessor.java index 09df865..181986b 100644 --- a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/MessageProcessor.java +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/MessageProcessor.java @@ -1,6 +1,5 @@ package cn.skcks.docking.gb28181.core.sip.message.processor; -import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,8 +14,10 @@ public interface MessageProcessor { class Method { public static final String REGISTER = "REGISTER"; + public static final String MESSAGE = "MESSAGE"; } + void init(); void process(RequestEvent requestEvent); default MessageFactory getMessageFactory() { diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/message/request/MessageRequestProcessor.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/message/request/MessageRequestProcessor.java new file mode 100644 index 0000000..a4d3615 --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/message/request/MessageRequestProcessor.java @@ -0,0 +1,77 @@ +package cn.skcks.docking.gb28181.core.sip.message.processor.message.request; + +import cn.skcks.docking.gb28181.common.xml.XmlUtils; +import cn.skcks.docking.gb28181.core.sip.gb28181.constant.CmdType; +import cn.skcks.docking.gb28181.core.sip.message.processor.message.request.dto.MessageDto; +import cn.skcks.docking.gb28181.core.sip.gb28181.constant.GB28181Constant; +import cn.skcks.docking.gb28181.core.sip.listener.SipListener; +import cn.skcks.docking.gb28181.core.sip.message.processor.MessageProcessor; +import cn.skcks.docking.gb28181.core.sip.message.sender.SipMessageSender; +import cn.skcks.docking.gb28181.core.sip.utils.SipUtil; +import cn.skcks.docking.gb28181.orm.mybatis.dynamic.model.DockingDevice; +import cn.skcks.docking.gb28181.service.docking.device.DockingDeviceService; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; + +@Slf4j +@RequiredArgsConstructor +@Component +public class MessageRequestProcessor implements MessageProcessor { + private final SipListener sipListener; + private final DockingDeviceService deviceService; + private final SipMessageSender sender; + + @PostConstruct + @Override + public void init() { + sipListener.addProcessor(Method.MESSAGE, this); + } + + @Override + public void process(RequestEvent requestEvent) { + SIPRequest request = (SIPRequest)requestEvent.getRequest(); + String deviceId = SipUtil.getUserIdFromFromHeader(request); + CallIdHeader callIdHeader = request.getCallIdHeader(); + + MessageDto messageDto = XmlUtils.parse(request.getRawContent(), MessageDto.class, GB28181Constant.CHARSET); + log.debug("接收到的消息 => {}", messageDto); + + DockingDevice device = deviceService.getDevice(deviceId); + String senderIp = request.getLocalAddress().getHostAddress(); + + if(device == null){ + log.info("未找到相关设备信息 => {}", deviceId); + Response response = response(request,Response.NOT_FOUND,"设备未注册"); + sender.send(senderIp,response); + return; + } + + if(messageDto.getCmdType().equalsIgnoreCase(CmdType.KEEPALIVE)){ + Response response = response(request,Response.OK,"OK"); + // 更新设备在线状态 + deviceService.online(device, response); + sender.send(senderIp,response); + } + } + + @SneakyThrows + public Response response(SIPRequest request, int status, String message){ + if (request.getToHeader().getTag() == null) { + request.getToHeader().setTag(SipUtil.generateTag()); + } + SIPResponse response = (SIPResponse)getMessageFactory().createResponse(status, request); + if (message != null) { + response.setReasonPhrase(message); + } + return response; + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/message/request/dto/MessageDto.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/message/request/dto/MessageDto.java new file mode 100644 index 0000000..a69725a --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/message/request/dto/MessageDto.java @@ -0,0 +1,19 @@ +package cn.skcks.docking.gb28181.core.sip.message.processor.message.request.dto; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.Data; + +@JacksonXmlRootElement(localName = "xml") +@Data +public class MessageDto { + private String cmdType; + + @JacksonXmlProperty(localName = "SN") + private String sn; + + @JacksonXmlProperty(localName = "DeviceID") + private String deviceId; + + private String status; +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/register/request/RegisterRequestProcessor.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/register/request/RegisterRequestProcessor.java index c81ba34..f6b0b18 100644 --- a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/register/request/RegisterRequestProcessor.java +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/message/processor/register/request/RegisterRequestProcessor.java @@ -4,7 +4,7 @@ import cn.hutool.core.date.DateUtil; import cn.skcks.docking.gb28181.config.sip.SipConfig; import cn.skcks.docking.gb28181.core.sip.dto.RemoteInfo; import cn.skcks.docking.gb28181.core.sip.dto.SipTransactionInfo; -import cn.skcks.docking.gb28181.core.sip.gb28181.constant.DefaultConstant; +import cn.skcks.docking.gb28181.core.sip.gb28181.constant.GB28181Constant; import cn.skcks.docking.gb28181.core.sip.gb28181.sip.GbSipDate; import cn.skcks.docking.gb28181.core.sip.listener.SipListener; import cn.skcks.docking.gb28181.core.sip.message.auth.DigestServerAuthenticationHelper; @@ -46,7 +46,8 @@ public class RegisterRequestProcessor implements MessageProcessor { private final DockingDeviceService dockingDeviceService; @PostConstruct - private void init(){ + @Override + public void init(){ sipListener.addProcessor(Method.REGISTER,this); } @@ -61,7 +62,7 @@ public class RegisterRequestProcessor implements MessageProcessor { SipUri uri = (SipUri)address.getURI(); String deviceId = uri.getUser(); log.debug("请求注册 设备id => {}", deviceId); - DockingDevice device = dockingDeviceService.getDeviceInfo(deviceId); + DockingDevice device = dockingDeviceService.getDevice(deviceId); String senderIp = request.getLocalAddress().getHostAddress(); RemoteInfo remoteInfo = SipUtil.getRemoteInfoFromRequest(request, false); log.debug("远程连接信息 => {}", remoteInfo); @@ -135,8 +136,8 @@ public class RegisterRequestProcessor implements MessageProcessor { if (device == null) { device = new DockingDevice(); device.setStreamMode(ListeningPoint.UDP); - device.setCharset(DefaultConstant.CHARSET); - device.setGeoCoordSys(DefaultConstant.GEO_COORD_SYS); + device.setCharset(GB28181Constant.CHARSET); + device.setGeoCoordSys(GB28181Constant.GEO_COORD_SYS); device.setDeviceId(deviceId); device.setOnLine(false); } else { @@ -144,10 +145,10 @@ public class RegisterRequestProcessor implements MessageProcessor { device.setStreamMode(ListeningPoint.UDP); } if (ObjectUtils.isEmpty(device.getCharset())) { - device.setCharset(DefaultConstant.CHARSET); + device.setCharset(GB28181Constant.CHARSET); } if (ObjectUtils.isEmpty(device.getGeoCoordSys())) { - device.setGeoCoordSys(DefaultConstant.GEO_COORD_SYS); + device.setGeoCoordSys(GB28181Constant.GEO_COORD_SYS); } } diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/service/docking/device/DockingDeviceService.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/service/docking/device/DockingDeviceService.java index 79480f9..1c1adaf 100644 --- a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/service/docking/device/DockingDeviceService.java +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/service/docking/device/DockingDeviceService.java @@ -16,7 +16,6 @@ import org.mybatis.dynamic.sql.SqlBuilder; import org.springframework.stereotype.Service; import javax.sip.message.Response; -import java.util.concurrent.TimeUnit; @Slf4j @Service @@ -32,7 +31,7 @@ public class DockingDeviceService { * @param deviceId 设备id * @return 设备信息 */ - public DockingDevice getDeviceInfo(String deviceId) { + public DockingDevice getDevice(String deviceId) { DockingDevice device = deviceCacheService.getDevice(deviceId); if (device == null) { device = dockingDeviceMapper @@ -65,7 +64,7 @@ public class DockingDeviceService { dockingDeviceMapper.insert(device); }); - getDeviceInfo(deviceId); + getDevice(deviceId); onlineCacheService.setOnline(deviceId, DeviceConstant.KEEP_ALIVE_INTERVAL * 3, DeviceConstant.UNIT); setTransaction(deviceId, response); }