JAIN SIP API详解与GB28181服务器实现

目录

JAIN SIP API

摘要

关于JAIN SIP API

API概述

maven坐标

类/接口

Message接口

Request接口

Response接口

即时通讯程序

TextClient代码概述

Message Processor

SIP协议栈

发送SIP请求

发送会话消息

接收SIP响应

接收SIP请求

处理错误

总结

GB28181SIP服务器

注册

保活

sip-server-demo代码概述

创建springboot项目

SIP协议栈

接收SIP请求响应

处理SIP请求

发送SIP请求

IPC接入

抓包分析

总结


JAIN SIP API

摘要

这篇文章展示了基于Java SE如何创建客户端侧的SIP应用。JAIN SIP API是一个强大的“SIP协议栈”。本文将通过一个简单的即时通讯程序以及一个GB28181协议的简单应用程序,详细的分析该技术。

关于JAIN SIP API

Java api for Integrated Networks (JAIN)是一个JCP工作组所管理的电信标准,Session Initiation Protocol(SIP)是一种标准的通信协议,将Java和SIP结合在一起,就得到了JAIN SIP API,这是一个标准的、功能强大的电信API。这个API通常用于客户端应用程序开发。其他基于容器的技术,如SIP Servlet API(参见BEA WebLogic SIP Server的例子),更适合于服务器端开发,但是在GB28181协议应用程序中我们也采用该API用作SIP服务器的开发实现IPC与联网平台的信令交互。

API概述

maven坐标

	<dependency>
    	<groupId>javax.sip</groupId>
   	 	<artifactId>jain-sip-ri</artifactId>
    	<version>1.3.0-91</version>
	</dependency>

类/接口

下面概述了JAIN SIP API实现中的主要类和接口。

Class / Interface描述
SipFactory / AddressFactory / HeaderFactory / MessageFactory工厂类来创建系统的各种对象。它们返回声明了标准接口的对象。
SipStack您需要的第一个接口,用于创建ListeningPoints和SipProviders。
ListeningPoint这个接口封装了一个传输/端口对(例如UDP/5060)。
SipProvider这个接口用来发送SIP消息。您还可以使用此接口为传入的SIP消息注册一个监听器。参见下面的SipListener。
SipListener您必须实现此接口以允许接收传入的SIP消息。
RequestEvent / ResponseEvent表示传入的SIP请求、响应。传递给SipListener进行处理。分别包含一个Request或Response对象。
TimeoutEvent表示传出请求没有回复时的失败条件。传递给SipListener进行处理。
IOExceptionEvent表示在发送外发请求时出现输入/输出问题时的失败条件。传递给SipListener进行处理。
Request / Response表示SIP请求、响应。两者都是Message接口的子接口。它们提供对报头、内容和SIP消息的其他部分的访问。
Dialog此接口的对象封装了一个SIP对话框。(提醒:在对话框中,所有消息都与同一个调用相关;对话通常以INVITE开始,以BYE结束。
ClientTransaction / ServerTransaction封装SIP事务。(提醒:事务以请求开始,以最终响应结束。事务通常存在于对话框中。)

Message接口

Message接口是SIP消息的基本接口,下面是可用方法的概述。

Method描述
void addHeader(Header) void setHeader(Header)将报头字段设置为SIP消息。第一种方法可用于可重复或具有多个值的标头,如Contact标头。第二个方法删除该类型的现有头,然后添加单个头值。
void removeHeader(Header)删除此类型的现有标头。
ListIterator getHeaderNames()返回所有头文件名称。
ListIterator getUnrecognizedHeaders()返回非标准报头类型的报头名称。
Header getHeader(String) ListIterator getHeaders(String)ListIterator getHeaders(字符串) 特定头的getter。第二种形式返回可重复标头的所有值,或具有多个值的标头,如Contact标头。
void setContent(Object, ContentTypeHeader)设置消息的有效负载以及Content-Type报头。如果类型是字符串,Content-Length也被设置,否则使用void setContentLength(ContentLengthHeader)。
byte [] getRawContent() Object getContent()检索消息的有效负载。
void removeContent()清空有效负载。
void setContentLength(ContentLengthHeader) ContentLengthHeader getContentLength() void setContentLanguage(ContentLanguageHeader) ContentLanguageHeader getContentLanguage() void setContentEncoding(ContentEncodingHeader) ContentEncodingHeader getContentEncoding() void setContentDisposition(ContentDispositionHeader) ContentDispositionHeader getContentDisposition()与有效负载相关的特殊头访问器。很少使用。
void setExpires(ExpiresHeader) ExpiresHeader getExpires()管理Expires报头。
void setSipVersion(String) String getSipVersion()字符串getSipVersion () SIP版本元素的访问器。很少使用,默认为SIP/2.0。
Object clone()创建消息的副本。很少使用。

Request接口

Message接口的子接口

Method描述
String getMethod() void setMethod(String)方法元素的访问器。可以是任何SIP方法,包括请求接口常量中的方法:ACK、BYE、CANCEL、INVITE、OPTIONS、REGISTER、NOTIFY、SUBSCRIBE、MESSAGE、REFER、INFO、PRACK和UPDATE。
URI getRequestURI() void setRequestURI(URI)请求URI的访问器,这是SIP请求的第一行。通常,这是SipURI的一个实例。

Response接口

Message接口的子接口。

Method描述
void setStatusCode() int getStatusCode()状态代码的访问器。这可以是任何SIP状态码,包括Response接口的常量成员中的状态码。这里有一些:RINGING (180), OK (200), BAD_REQUEST(400),等等。
void setReasonPhrase(String) String getReasonPhrase()访问器,用于人类可读的状态代码解释。

即时通讯程序

TextClient是一个即时消息传递应用程序,可以通过SIP协议发送和接收文本消息。此应用程序的一个实例可以向另一个实例发送消息,但从理论上讲,此客户机可用于向其他类型的SIP即时消息传递客户机,甚至SIP服务器应用程序发送消息。如下图所示,SIP客户端yrz向另一个SIP客户端yz发送了一条”我是yrz2023年4月18日13:46:22“的消息,随后SIP客户端yz回复了一条”yz收到2023年4月18日13:46:22“的消息。

TextClient代码概述

两个类和一个接口组成了整个TextClient代码。下表介绍:

Class / Interface描述
TextClient主类,包含应用程序小部件的Swing窗口。
SipLayer它负责所有SIP通信。它由TextClient类实例化,并通过MessageProcessor接口回调它。
MessageProcessor回调接口(观察者模式),用于将SipLayer与其容器解耦。

Message Processor

创建MessageProcessor接口,将SIP层与GUI层分离。TextClient类实现该接口,其构造函数将SipLayer对象作为参数,您将能够使用SipLayer对象将信息发送回GUI。

public interface MessageProcessor
{
    // 请求回调方法
    void processMessage(String sender, String message);
    // 请求错误回调方法
    void processError(String errorMessage);
    // 响应回调方法
    void processInfo(String infoMessage);
}

SIP协议栈

让我们开始编写SipLayer类。TextClient必须能够接收来自其他SIP端点的异步消息。这个类实现了SipListener接口来处理传入的消息:

public class SipLayer implements SipListener {
	...
}

SipListener接口方法如下:

public interface SipListener extends EventListener {
    void processRequest(RequestEvent var1);

    void processResponse(ResponseEvent var1);

    void processTimeout(TimeoutEvent var1);

    void processIOException(IOExceptionEvent var1);

    void processTransactionTerminated(TransactionTerminatedEvent var1);

    void processDialogTerminated(DialogTerminatedEvent var1);
}

在本例中,用于处理传入消息的最重要的方法显然是processRequest()和processResponse()。接下来是存储稍后需要的对象的两个字段:username和messageProcessor,这些与SIP API没有直接关系,但是在本例中需要它们。第一个是前面讨论过的MessageProcessor对象,用于回调方法将消息发回给GUI,username用于随时保留用户名,这两个字段有getter和setter方法。

private MessageProcessor messageProcessor;
private String username;

接下来是构造函数,一种启动JAIN SIP API的经典方法——建立一堆以后会有用的对象(工厂和SIP协议栈实例),TextClient就是采用的这种方法。

private SipStack sipStack;
    
private SipFactory sipFactory;
    
private AddressFactory addressFactory;
    
private HeaderFactory headerFactory;
    
private MessageFactory messageFactory;
    
private SipProvider sipProvider;

public SipLayer(String username, String ip, int port) throws	PeerUnavailableException, 
TransportNotSupportedException,InvalidArgumentException, ObjectInUseException, TooManyListenersException {
    
      setUsername(username);
    
      sipFactory = SipFactory.getInstance();
    
      sipFactory.setPathName("gov.nist");
    
      Properties properties = new Properties();
    
      properties.setProperty("javax.sip.STACK_NAME",
    
              "TextClient");
    
      properties.setProperty("javax.sip.IP_ADDRESS",
    
              ip);
    
      sipStack = sipFactory.createSipStack(properties);
    
      headerFactory = sipFactory.createHeaderFactory();
    
      addressFactory = sipFactory.createAddressFactory();
    
      messageFactory = sipFactory.createMessageFactory();
    
      ...

SipFactory用于实例化SipStack实现,但由于可能有多个实现,因此必须通过setPathName()方法命名您想要的那个实现。名称“gov.nist”表示您获得的SIP堆栈。

SipStack对象具有许多属性。至少,您必须设置堆栈名称。所有其他属性都是可选的。在这里,我设置了一个由堆栈使用的IP地址,用于一台计算机有多个IP地址的情况。注意,这里有标准属性(所有SIP API实现都必须支持)和非标准属性(依赖于实现)。

下一步是创建一对ListeningPoint和SipProvider对象。这些对象提供了发送和接收消息的通信功能。TCP有一组,UDP有一组。这也是你选择SipLayer作为传入SIP消息的监听器的地方:

...
    
      ListeningPoint tcp = sipStack.createListeningPoint(port, "tcp");
    
      ListeningPoint udp = sipStack.createListeningPoint(port, "udp");
    
      sipProvider = sipStack.createSipProvider(tcp);
    
      sipProvider.addSipListener(this);
    
      sipProvider = sipStack.createSipProvider(udp);
    
      sipProvider.addSipListener(this);
    
    }

构造函数就是这样结束的。您已经使用JAIN SIP API创建了一个SipStack实例、一堆工厂、两个listeningpoint和一个SipProvider。这些对象将在接下来的方法中用于发送和接收消息。

发送SIP请求

现在让我们编写一个使用JAIN SIP API发送SIP消息的方法,在此之前你必须非常了解SIP协议。SIP API是相当低级的抽象,在大多数情况下,不使用默认值或隐藏头、请求uri或SIP消息的内容。这种设计的优点是您可以完全控制SIP消息所包含的内容。

发送一个SIP请求大致分为四个部分:

  • 创建主要元素

  • 创建消息

  • 完整的消息

  • 发送消息

使用JAIN SIP API构造消息最少需要以下主要SIP元素:

  • 请求URI

  • 方法

  • 通话身份头

  • CSeq头

  • 从标题

  • Via报头数组

  • Max-forwards头

下面的代码片段创建了所有这些元素:

public void sendMessage(String to, String message) throws
                ParseException, InvalidArgumentException, SipException {
    
            SipURI from = addressFactory.createSipURI(getUsername(),
                    getHost() + ":" + getPort());
        Address fromNameAddress = addressFactory.createAddress(from);
            fromNameAddress.setDisplayName(getUsername());
            FromHeader fromHeader =
                    headerFactory.createFromHeader(fromNameAddress,
                            "textclientv1.0");
    
            String username = to.substring(to.indexOf(":")+1, to.indexOf("@"));
            String address = to.substring(to.indexOf("@")+1);
    
            SipURI toAddress =
                    addressFactory.createSipURI(username, address);
            Address toNameAddress = addressFactory.createAddress(toAddress);
            toNameAddress.setDisplayName(username);
            ToHeader toHeader =
                    headerFactory.createToHeader(toNameAddress, null);
    
            SipURI requestURI =
                    addressFactory.createSipURI(username, address);
            requestURI.setTransportParam("udp");
    
            ArrayList viaHeaders = new ArrayList();
            ViaHeader viaHeader =
                    headerFactory.createViaHeader(
                            getHost(),
                            getPort(),
                            "udp",
                            null);
            viaHeaders.add(viaHeader);
    
            CallIdHeader callIdHeader = sipProvider.getNewCallId();
    
            CSeqHeader cSeqHeader =
                    headerFactory.createCSeqHeader(1, Request.MESSAGE);
    
            MaxForwardsHeader maxForwards =
                    headerFactory.createMaxForwardsHeader(70);
            ...

我使用在构造函数HeaderFactory和AddressFactory中创建的工厂来实例化这些元素。接下来让我们实例化实际的SIP消息本身,传入之前创建的所有元素:

Request request =  messageFactory.createRequest(
            requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,
            fromHeader, toHeader, viaHeaders,       maxForwards);
    ...

注意,这一步使用了MessageFactory。然后,让我们向消息添加其他元素:联系人标头和消息的内容(有效负载),也可以添加自定义标题。

SipURI contactURI = addressFactory.createSipURI(getUsername(),
                    getHost());
            contactURI.setPort(getPort());
            Address contactAddress = addressFactory.createAddress(contactURI);
            contactAddress.setDisplayName(getUsername());
            ContactHeader contactHeader =
                    headerFactory.createContactHeader(contactAddress);
            request.addHeader(contactHeader);
            ContentTypeHeader contentTypeHeader =
                    headerFactory.createContentTypeHeader("text", "plain");
            request.setContent(message, contentTypeHeader);
            ...

最后,使用SipProvider实例发送消息:

sipProvider.sendRequest(request);    }

发送会话消息

你在会话外发送我们的信息,这意味着消息之间没有关联,这对于TextClient这样的简单即时消息传递应用程序来说效果很好。另一种方法是使用INVITE消息创建一个会话,然后在该会话内发送消息。TextClient不使用这种技术,但是是值得学习的东西,本小节描述了如何做到这一点。

在会话中发送消息需要创建Dialog和Transaction对象。在初始消息(即创建会话的消息)上,不使用提供程序发送消息,而是实例化一个Transaction,然后从中获取Dialog。您保留Dialog引用以供以后使用。然后使用事务发送消息:

ClientTransaction trans = sipProvider.getNewClientTransaction(invite);
    dialog = trans.getDialog();
    trans.sendRequest();

稍后,当您希望在同一个会话中发送新消息时,您可以使用前面的Dialog对象来创建一个新请求。然后,您可以对请求进行消息处理,最后,使用Transaction发送消息。

request = dialog.createRequest(Request.MESSAGE);
    request.setHeader(contactHeader);
    request.setContent(message, contentTypeHeader);
  
    ClientTransaction trans = sipProvider.getNewClientTransaction(request);
    trans.sendRequest();

从本质上讲,在现有会话中发送消息时,您跳过了“创建主要元素”步骤。当您使用INVITE创建对话框时,不要忘记在对话框结束时发送一个BYE消息来清理它。此技术还用于刷新注册和订阅。

在前面,您已经看到了SipListener接口,其中包含processDialogTerminated()和processTransactionTerminated()方法。它们分别在对话框和事务结束时自动调用。通常,实现这些方法是为了清理(例如,丢弃Dialog和Transaction实例)。您将把这两个方法留空,因为在TextClient中不需要它们。

接收SIP响应

前面,您注册了传入消息的监听器。监听器接口SipListener包含方法processResponse(),当SIP响应消息到达时,由SIP协议栈调用该方法。processResponse()接受一个ResponseEvent类型的参数,它封装了一个Response对象。

public void processResponse(ResponseEvent evt) {         
	Response response = evt.getResponse();         
	int status = response.getStatusCode();          
	if( (status >= 200) && (status < 300) ) { //Success!                 
	messageProcessor.processInfo("--Sent");                 
	return;         
	}
    messageProcessor.processError("Previous message not sent: " + status); 
}

在此方法中,您将检查先前MESSAGE消息的响应是否表示成功(2xx范围的状态码)或错误(否则)。然后通过回调接口将此信息转发给用户。

通常,您只读取processResponse()方法中的Response对象。唯一的例外是对INVITE消息的成功响应;在这种情况下,你必须发送一个ACK请求,就像这样:

Dialog dialog = evt.getClientTransaction().getDialog();
Request ack =  dialog.createAck();
dialog.sendAck(ack);

接收SIP请求

接收SIP请求消息与接收响应一样简单。您只需实现SipListener接口的另一个方法processRequest(), SIP堆栈将自动调用它。该方法的唯一参数是RequestEvent对象,其中包含Request对象。这是你之前见过的相同类型,它有相同的方法。但是,您不应该在传入请求上设置任何字段,因为这没有多大意义。

processRequest()的典型实现就是分析请求,然后创建并发回适当的响应:

public void processRequest(RequestEvent evt) {         
	Request req = evt.getRequest();          
	String method = req.getMethod();         
	if( ! method.equals("MESSAGE")) { //bad request type.                 
		messageProcessor.processError("Bad request type: " + method);                 
		return;         
	}          
	FromHeader from = (FromHeader)req.getHeader("From");         
	messageProcessor.processMessage(from.getAddress().toString(), new String(req.getRawContent()));         	Response response=null;         
	try { //Reply with OK
		response = messageFactory.createResponse(200, req);                 
		ToHeader toHeader = (ToHeader)response.getHeader(ToHeader.NAME);                 
		toHeader.setTag("888"); //Identifier, specific to your application                 
		ServerTransaction st = sipProvider.getNewServerTransaction(req);           				
		st.sendResponse(response);	
	} catch (Throwable e) {                 
		e.printStackTrace();                 
		messageProcessor.processError("Can't send OK reply.");         
	} 
}

在这种情况下,您总是用一个成功响应(200)来回复,但是您也可以发回任何错误响应(通常是4xx范围)。

处理错误

SipListener接口中还有其他尚未实现的方法。当由于特定原因无法发送请求时,由SIP协议调用它们。例如,当接收消息的端点没有及时应答时,将调用processTimeout()。这是一种没有响应的特殊情况,因此没有可用的response对象。TimeoutEvent参数包含超时请求的ClientTransaction,如果需要,可以使用该参数链接回原始请求。在这个实现中,你只需使用回调接口通知用户:

public void processTimeout(TimeoutEvent evt) {         
	messageProcessor.processError("Previous message not sent: " + "timeout"); 
}

类似地,Input/Output (IO)错误的处理方法如下:

public void processIOException(IOExceptionEvent evt) {         
	messageProcessor.processError("Previous message not sent: " + "I/O Exception"); 
}

总结

本文概述了JAIN SIP API,并展示了如何编写一个简单的应用程序来使用这项技术。现在,您应该对可用的api有了很好的了解,并且知道如何使用SIP编写自己的IM客户机。

以上内容主要来自ORACLE官网《An Introduction to the JAIN SIP API》文章,TextClient源码下载地址也在文章提供,感兴趣的同学可以阅读原文,文章地址:An Introduction to the JAIN SIP API。

下面将该API应用到安防领域实现一个能够满足GB28181协议的SIP服务器。

GB28181SIP服务器

在GB28181-2022协议规范中“9控制、传输流程和协议接口”中规定了IPC注册、注销、点播、状态信息报送等控制的命令流程与协议接口,下面我们将按照GB28181流程,采用JAIN SIP API实现IPC的向SIP服务器的注册与状态信息报送(保活)。

注册

命令流程

命令流程截图自GB28181-2022,见下图:

保活

命令流程

命令流程截图自GB28181-2022,见下图:

协议接口

协议接口描述截图自GB28181-2022,见下图:

sip-server-demo代码概述

创建springboot项目

创建一个springboot项目并引入JAIN SIP API依赖。

SIP协议栈

创建SipLayer声明CommandLineRunner接口,项目启动时会建立一堆以后会有用的对象:SipFactory、SipStack、ListeningPoint,同时创建TCP与UDP监听器用来兼容IPC的TCP/UDP接入。

SipLayer类注入SipConfig对象,该对象配置了SIP服务器的ip、端口、域名、id和密码。

SipLayer类注入SipServerListener,SipServerListener接口继承于SipListener,SipServerListener的子类为SipServerListenerImpl,SipServerListenerImpl为实现SIP请求响应的处理。

接收SIP请求响应

SipServerListenerImpl类实现了SipListener接口,重写processRequest()与processResponse()方法,来接收SIP请求与响应。该类采用了类似观察者模式的设计思路,声明了两个线程安全的容器reqHandlerMap与respHandlerMap用来存放不同的SIP请求响应的真实处理对象,例如processRequest()接收到一个REGISTER请求,利用java继承与多态的特性,processRequest()方法根据SIP方法类型为key获取到真实处理对象,最后由真实处理对象处理REGISTER请求。

在真实的平台与IPC进行信令交互时,会面临并发处理多种SIP请求响应的场景,所以在processRequest()与processResponse()方法上使用@Aync()注解,实现异步处理SIP信令。

处理SIP请求

SipReqHandler接口的实现类有两个RegisterReqHandler和KeepaliveReqHandler,分别实现IPC的注册与保活,代码实现流程请参照该小节的命令流程部分,最后我们将进行抓包分析整个信令的交互流程。

我们发现GB28181中,有很多控制传输流程都是通过MESSAGE方法+MANSCDP命令集实现的,所以我们要在接收到IPC的MESSAGE方法时,解析MANSCDP命令集,解析到cmdType = "Keepalive"的请求,才是保活请求,然后回复给IPC200,其他的MESSAGE请求这里暂时不处理。

发送SIP请求

SipSender类实现了SIP消息报文的封装,通过sendResponse()方法回复IPC消息。

IPC接入

首先启动SIP服务器,查看SIP服务器的配置信息:

#SIP
# SIP服务器IP
sip.ip=10.192.33.34
# SIP服务监听的端口
sip.port=5060
# SIP域
sip.domain=34020000
# SIP服务器国标ID
sip.id=34020000001320000010
# SIP服务器密码
sip.password=admin123

IPC平台接入配置需要配置SIP服务器信息:

 IPC平台接入的密码为SIP服务器密码,用于服务器校验,校验正确才能实现IPC的注册。IPC每间隔60秒发送一次心跳信息,观察SIP服务器日志,满足GB28181规定命令流程,下面抓包分析信令交互流程。

SIP服务器日志:

 

 

抓包分析

注册

REGISTER sip:34020000001320000010@34020000 SIP/2.0
Via: SIP/2.0/UDP 10.192.33.95:5060;rport;branch=z9hG4bK863117711
From: <sip:34020000001320000002@34020000>;tag=294565749
To: <sip:34020000001320000002@34020000>
Call-ID: 1973051184
CSeq: 1 REGISTER
Contact: <sip:34020000001320000002@10.192.33.95:5060>
Max-Forwards: 70
User-Agent: IP Camera
Expires: 3600
Content-Length: 0

SIP/2.0 401 Unauthorized
CSeq: 1 REGISTER
Call-ID: 1973051184
From: <sip:34020000001320000002@34020000>;tag=294565749
To: <sip:34020000001320000002@34020000>
Via: SIP/2.0/UDP 10.192.33.95:5060;rport=5060;branch=z9hG4bK863117711;received=10.192.33.95
WWW-Authenticate: Digest realm="34020000",qop="auth",nonce="1ba00522b15b098aa2c05150cdb0df31",algorithm=MD5
User-Agent: sip-server-yrz
Content-Length: 0

REGISTER sip:34020000001320000010@34020000 SIP/2.0
Via: SIP/2.0/UDP 10.192.33.95:5060;rport;branch=z9hG4bK713030866
From: <sip:34020000001320000002@34020000>;tag=294565749
To: <sip:34020000001320000002@34020000>
Call-ID: 1973051184
CSeq: 2 REGISTER
Contact: <sip:34020000001320000002@10.192.33.95:5060>
Authorization: Digest username="34020000001320000002", realm="34020000", nonce="1ba00522b15b098aa2c05150cdb0df31", uri="sip:34020000001320000010@34020000", response="ff34c4434d132ad9b956c729aa229194", algorithm=MD5, cnonce="0a4f113b", qop=auth, nc=00000001
Max-Forwards: 70
User-Agent: IP Camera
Expires: 3600
Content-Length: 0

SIP/2.0 200 OK
CSeq: 2 REGISTER
Call-ID: 1973051184
From: <sip:34020000001320000002@34020000>;tag=294565749
To: <sip:34020000001320000002@34020000>
Via: SIP/2.0/UDP 10.192.33.95:5060;rport=5060;branch=z9hG4bK713030866;received=10.192.33.95
Date: 2023-04-19T11:29:33.703
Contact: <sip:34020000001320000002@10.192.33.95:5060>
Expires: 3600
User-Agent: sip-server-yrz
Content-Length: 0

流程分析:

  1. IPC发起REGISTER请求,未携带Authorization认证信息。

  2. SIP服务器回复401与认证加密算法。

  3. IPC重新发起REGISTER并携带Authorization认证信息。

  4. SIP服务器认证成功后回复200,IPC注册成功。

保活

MESSAGE sip:34020000001320000010@34020000 SIP/2.0
Via: SIP/2.0/UDP 10.192.33.95:5060;rport;branch=z9hG4bK1171736073
From: <sip:34020000001320000002@34020000>;tag=699092543
To: <sip:34020000001320000010@34020000>
Call-ID: 776784695
CSeq: 20 MESSAGE
Content-Type: Application/MANSCDP+xml
Max-Forwards: 70
User-Agent: IP Camera
Content-Length:   182

<?xml version="1.0" encoding="GB2312"?>
<Notify>
<CmdType>Keepalive</CmdType>
<SN>3634867</SN>
<DeviceID>34020000001320000002</DeviceID>
<Status>OK</Status>
<Info>
</Info>
</Notify>
SIP/2.0 200 OK
CSeq: 20 MESSAGE
Call-ID: 776784695
From: <sip:34020000001320000002@34020000>;tag=699092543
To: <sip:34020000001320000010@34020000>;tag=1681876509157
Via: SIP/2.0/UDP 10.192.33.95:5060;rport=5060;branch=z9hG4bK1171736073;received=10.192.33.95
User-Agent: sip-server-yrz
Content-Length: 0

流程分析:

  1. IPC发起MESSAGE请求并携带设备ID。

  2. SIP服务器回复200。

总结

SIP服务器源码私信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/135233.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HDRP图形入门:HDRP渲染管线depth翻转

新项目开坑HDRP渲染管线&#xff0c;花了些时间把项目开发框架和图形工作流更新到最新版本&#xff0c;其间发现HDRP中深度信息和buildin渲染管线翻转了。 以前的buildin渲染管线&#xff0c;距离摄像机越近depth->0&#xff0c;越远depth->1&#xff0c;这也很好理…

2.3 CE修改器:浮点数扫描

本关需要使用 Cheat Engine 工具对浮点数进行扫描&#xff0c;完成修改任务。浮点数是一种带有小数点的数值&#xff0c;通过“浮点数”扫描方式进行修改。本关中&#xff0c;健康值为单精度浮点数&#xff0c;弹药值为双精度浮点数&#xff0c;需要将这两项数值都修改为 5000 …

【Linux】八、进程通信

进程通信的介绍 目的 数据传输&#xff1a;一个进程将它的数据发送给另一个进程&#xff1b; 资源共享&#xff1a;多个进程间共享资源&#xff1b; 通知事件&#xff1a;一个进程向另一个或一组进程发送消息&#xff0c;同时事件如&#xff0c;进程终止时要通知父进程&#xf…

web前端开发第一次Dreamweave课堂练习/html练习代码《社会主义核心价值观》

目标图片&#xff1a; 文字素材&#xff1a; 社会主义核心价值观 Socialist Core Values 富强、民主、文明、和谐是国家层面的价值目标。 自由、平等、公正、法治是社会层面的价值取向。 爱国、敬业、诚信、友善是公民个人层面的价值准则。 Core socialist values are the…

联系作者方式的教程

首先你应该目前是在付费资源运行效果的展示文章页面&#xff0c;如下所示 然后一直往下滑&#xff0c;滑到这个文章的最下面&#xff0c;就可以看到我的推广名片&#xff0c;最后点击这个名片就可以获取到我的联系方式了~

[云原生案例2.4 ] Kubernetes的部署安装 【通过Kubeadm部署Kubernetes高可用集群】

文章目录 1. 基本架构及前置准备1.1 基本架构1.2 前置准备 2. 系统初始化操作 ---- 所有节点2.1 关闭防火墙、selinux和swap分区2.1.1 关闭防火墙和selinux2.1.2 关闭交换分区 2.2 修改主机名&#xff0c;添加域名映射2.2.1 修改主机名2.2.2 修改本地hosts文件 2.3 内核升级2.4…

MGA-WPA

作者未提供代码

YOLO目标检测——水果检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;水果分类检测数据集的应用场景主要包括农贸市场监管、水果品质检测、超市零售管理等数据集说明&#xff1a;水果分类检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含有苹果香蕉橙子图片标签说明&#xff1a;使…

【Servlet】 三

本文主要介绍了基于serlvet的表白墙项目的编写. (附完整代码) 一.JS基础 作为后端开发,对于前端的要求是能前端代码能看懂七七八八 . JS是一个动态弱类型的编程语言 1. let/war定义变量 (推荐使用let) 2.querySelector是浏览器提供api , 能够获取到页面的元素的 (js的目的就…

CV计算机视觉每日开源代码Paper with code速览-2023.11.9

精华置顶 墙裂推荐&#xff01;小白如何1个月系统学习CV核心知识&#xff1a;链接 点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【3D目标检测】3DiffTection: 3D Object Detection with …

JAVA毕业设计110—基于Java+Springboot+Vue的房屋租赁系统小程序(源码+数据库)

基于JavaSpringbootVue的房屋租赁系统小程序(源码数据库)110 一、系统介绍 本系统前后端分离 本系统分为用户、房东、超级管理员三种角色 1、用户&#xff1a; 登录、注册、房屋搜索、房屋收藏、看房预约、租房申请、租房记录、看房记录、收藏记录、我的消息、个人信息修改…

企业培训服务预约小程序的作用是什么

企业在经营过程中往往会遇到人才培养进展缓慢、客户难以寻找维系、经营缺乏管理等痛点&#xff0c;基于此&#xff0c;相关企业培训机构或个人有着较高需求&#xff0c;但也同样面临着一些难题。 缺少转化方式&#xff0c;无法促进用户购买或预约咨询服务&#xff0c;向外扩展…

Golang 中的 Context 包

简介 今天&#xff0c;我们将讨论 Go 编程中非常重要的一个主题&#xff1a;context 包。如果你现在觉得它很令人困惑&#xff0c;不用担心 — 在本文结束时&#xff0c;你将像专家一样处理 context&#xff01; 想象一下&#xff0c;你在一个主题公园&#xff0c;兴奋地准备…

计算机网络基础知识-网络协议

一:计算机网络层次划分 1. 网络层次划分 2. OSI七层网络模型 1)物理层(Physical Layer):及硬件设备,物理层确保原始的数据可在各种物理媒体上传输,常见的设备名称如中继器(Repeater,也叫放大器)和集线器; 2)数据链路层(Data Link Layer):数据链路层在物理层提…

ThinkPHP图片处理之压缩图片大小,图片处理之图片水印(添加平铺文字水印,并设置文字之间的间距和文字的角度)

安装扩展 使用Composer安装图像处理类库 composer require topthink/think-image在对应的控制器内引入Image use think\Image;图片处理之压缩图片大小 public function upload(){$file request()->file(image);// 将前端传过来的图片移动到项目目录下$info $file->…

CS224W6.1——介绍图神经网络GNN

之前我们讨论了一些节点嵌入技术&#xff0c;它们可以通过随机游走的过程学习与任务无关的特征。从这篇开始&#xff0c;我们介绍了令人兴奋的图神经网络技术&#xff0c;该技术基于图结构用多层非线性变换对节点特征进行编码。图神经网络在各种任务中表现出非凡的性能&#xf…

BEVFormer 论文阅读

论文链接 BEVFormer BEVFormer&#xff0c;这是一个将Transformer和时间结构应用于自动驾驶的范式&#xff0c;用于从多相机输入中生成鸟瞰&#xff08;BEV&#xff09;特征利用查询来查找空间/时间&#xff0c;并相应地聚合时空信息&#xff0c;从而为感知任务提供更强的表示…

功能案例 -- 通过开关,改变白天和黑夜

效果展示 代码展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><style>:root {--default-bac-color: #f…

ArduPilot开源代码之Aocoda-RC F405V2 Mark4 7“ PNP适配

ArduPilot开源代码之Aocoda-RC F405V2 Mark4 10"适配 1. 源由2. 安装2.1 Installing Ground Station (GCS) software2.2 Autopilot System Assembly Instructions2.3 Loading Firmware2.4 Connect Mission Planner to AutoPilot 3. 配置3.1 Basic System Overview3.2 Fram…

web前端开发第4次Dreamweave课堂练习/html练习代码《出版界推出一批纪念抗美援朝胜利70周年主题图书》

目标图片&#xff1a; 文字素材&#xff1a; 出版界推出一批纪念抗美援朝胜利70周年主题图书 2023-08-01来源&#xff1a;新华社 为纪念抗美援朝战争胜利70周年&#xff0c;出版界集中推出了“抗美援朝亲历记丛书”《新中国立国之战——抗美援朝战争的回顾与思考》《毛泽东与…