JAIN SIP API详解与GB28181服务器实现【保姆级源码教程】

目录

一 JAIN SIP API

1 摘要

2 关于JAIN SIP API

3 API概述

3.1 maven坐标

3.2 类/接口

3.3 Message接口

3.4 Request接口

3.5 Response接口

4 即时通讯程序

4.1 TextClient代码概述

4.2 Message Processor

4.3 SIP协议栈

4.4 发送SIP请求

4.5 发送会话消息

4.6 接收SIP响应

4.7 接收SIP请求

4.8 处理错误

4.9 小节

二 GB28181SIP服务器——MSH

1 简介

2 GB28181

2.1 注册

2.2 保活

2.2.1 命令流程

2.2.2 协议接口

2.3 MSH代码概述

2.3.1 创建springboot项目

2.3.2 SIP协议栈

2.3.3 接收SIP请求响应

2.3.4 处理SIP请求

2.3.5 发送SIP请求

2.3.6 IPC接入

2.3.7 抓包与流程分析

2.3.7.1 注册

2.3.7.2 保活

3 小结

3.1 SIP服务器

3.2 WEB服务器


一 JAIN SIP API

1 摘要

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

2 关于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与联网平台的信令交互。

3 API概述

3.1 maven坐标

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

3.2 类/接口

下面概述了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事务。(提醒:事务以请求开始,以最终响应结束。事务通常存在于对话框中。)

3.3 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() 创建消息的副本。很少使用。

3.4 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的一个实例。

3.5 Response接口

Message接口的子接口。

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

4 即时通讯程序

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

4.1 TextClient代码概述

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

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

4.2 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);
}

4.3 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。这些对象将在接下来的方法中用于发送和接收消息。

4.4 发送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);    }

4.5 发送会话消息

你在会话外发送我们的信息,这意味着消息之间没有关联,这对于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中不需要它们。

4.6 接收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);

4.7 接收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范围)。

4.8 处理错误

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"); 
}

4.9 小节

本文概述了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服务器——MSH

1 简介

SIP(Session Initiation Protocol,会话发起协议)是一种基于文本的网络通信协议,主要用于实现语音、视频和数据等多种媒体资源的实时传输。SIP信令在以下应用领域和行业中发挥着重要作用:
1.语音通信:SIP协议可用于固定电话、移动电话和网络电话之间的通话,实现电话拨号、通话建立、通话保持和通话结束等功能。
2.视频通信:通过SIP协议,用户可以实现音视频通话、视频会议和协同工作等应用,满足企业和个人之间的沟通需求。
3.即时通讯:SIP协议可应用于即时通讯领域,提供文本、图片、语音和视频等丰富的通信方式,如微信、WhatsApp等。
4.网络电视和多媒体广播:SIP协议可用于实现点播、直播和时移电视等多媒体服务,满足用户对多媒体内容的需求。
5.智能家居:通过SIP协议,可以实现家庭设备之间的互联互通,如智能音响、智能摄像头、智能照明等,提升家居生活的便捷性和舒适度。
6.企业通信:SIP协议可应用于企业内部通信系统,实现电话交换、电话会议、呼叫中心等功能,提高企业通信效率。
7.物联网:SIP协议可用于物联网设备之间的通信,实现智能控制、远程监控和数据分析等应用。
公共服务:SIP协议在公共安全、紧急救援、交通管理等领域具有广泛应用,提高公共服务的质量和效率。
8.教育:SIP协议可以实现远程教学、在线课堂和视频讲座等功能,拓展教育领域的发展空间。
9.医疗:通过SIP协议,可以实现远程诊断、视频咨询和在线挂号等医疗服务,提高医疗资源的利用效率。

本demo(项目)是JAIN SIP API的应用,适用于安防领域,当然如果你是一位其他领域的从业者,该项目代码也会起到举一反三、抛砖引玉的作用。

本项目不仅仅包含了SIP服务器,还有流媒体服务器和WEB服务器的实现,是一个完整的程序,方便实现二次开发与功能拓展。下文仅仅对SIP服务器的实现做简单的介绍。

2 GB28181

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

2.1 注册

2.2 保活

2.2.1 命令流程

2.2.2 协议接口

2.3 MSH代码概述

2.3.1 创建springboot项目

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

2.3.2 SIP协议栈

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

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

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

2.3.3 接收SIP请求响应

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

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

2.3.4 处理SIP请求

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

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

2.3.5 发送SIP请求

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

2.3.6 IPC接入

1.首先启动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

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

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

3.SIP服务器日志:

 

 

2.3.7 抓包与流程分析
2.3.7.1 注册

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:[email protected]: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:[email protected]: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:[email protected]: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注册成功。

2.3.7.2 保活

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。

3 小结

3.1 SIP服务器

目前实现的为注册、保活、设备信息查询、目录查询、点播。下图为VLC播放取流播放截图。

3.2 WEB服务器

目前实现点播、通道同步、flv、hls格式流web预览。

代码持续更新中...

需要SIP服务器源码请私信我^-^

猜你喜欢

转载自blog.csdn.net/qq_27890899/article/details/130968649
sip