Java SIP Client

news2024/9/22 5:43:28

采用JAIN SIP API实现一个SIP客户端实现向SIP服务器注册。SIP服务器可以为FreeSWITCH也可以为满足GB28181的SIP平台。话不多说直接看注册流程图:

在这里插入图片描述

代码实现:

  1. 创建maven工程添加依赖

    <dependencies>
        <dependency>
          <groupId>javax.sip</groupId>
          <artifactId>jain-sip-ri</artifactId>
          <version>1.3.0-91</version>
        </dependency>
    
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.7.25</version>
        </dependency>
      </dependencies>
    
  2. 构造SIP协议栈

       	private static final String USERNAME = "1001";
    	private static final String PASSWORD = "1234";
    	private static final String IP = "xx.xx.xx.xx";
    	private static final Integer PORT = 5061;
    	private static final String SIP_SERVER_IP = "192.168.130.131";
    	private static final Integer SIP_SERVER_PORT = 5060;
        public SipLayer()
    	    throws PeerUnavailableException, TransportNotSupportedException,
    	    InvalidArgumentException, ObjectInUseException,
    	    TooManyListenersException {
    		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);
    		//DEBUGGING: Information will go to files
    		properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
    		properties.setProperty("gov.nist.javax.sip.SERVER_LOG",
    			"freeswitch_sip_client.txt");
    		properties.setProperty("gov.nist.javax.sip.DEBUG_LOG",
    			"freeswitch_sip_client.log");
    		// 成员属性初始化
    		sipStack = sipFactory.createSipStack(properties);
    		headerFactory = sipFactory.createHeaderFactory();
    		addressFactory = sipFactory.createAddressFactory();
    		messageFactory = sipFactory.createMessageFactory();
    		// ListeningPoint此接口封装传输/端口对(例如 UDP/5060)。
    		ListeningPoint tcp = sipStack.createListeningPoint(PORT, "tcp");
    		ListeningPoint udp = sipStack.createListeningPoint(PORT, "udp");
    		// 此接口用于发送 SIP消息
    		sipProvider = sipStack.createSipProvider(tcp);
    		// 为传入 SIP消息注册侦听器
    		sipProvider.addSipListener(this);
    		sipProvider = sipStack.createSipProvider(udp);
    		sipProvider.addSipListener(this);
        }
    
  3. 发起REGISTER请求

    public void register() throws Exception {
    		Request request = createRequest(null);
    		messageProcessor.processInfo(request.toString());
    		sipProvider.sendRequest(request);
        }
    
    
    private Request createRequest(AuthorizationHeader authorizationHeader) throws Exception{
    		// 组装sip协议消息
    		SipURI from = addressFactory.createSipURI(USERNAME, IP);
    		Address fromNameAddress = addressFactory.createAddress(from);
    		// fromNameAddress.setDisplayName(getUsername());
    		fromNameAddress.setDisplayName(USERNAME);
    		// Address address, String tag
    		FromHeader fromHeader = headerFactory.createFromHeader(fromNameAddress,
    				USERNAME + "v1.0");
    		SipURI toAddress = addressFactory.createSipURI(USERNAME, IP);
    		Address toNameAddress = addressFactory.createAddress(toAddress);
    		toNameAddress.setDisplayName(USERNAME);
    		ToHeader toHeader = headerFactory.createToHeader(toNameAddress, null);
    		// Request-URL
    		SipURI requestURI = addressFactory.createSipURI(null, SIP_SERVER_IP + ":" + SIP_SERVER_PORT);
    		REQUEST_URL = requestURI.toString();
    		requestURI.setTransportParam("udp");
    		ArrayList viaHeaders = new ArrayList();
    		// Via头域是被服务器插入request中,用来检查路由环的,并且可以使response根据via找到返回的路
    		ViaHeader viaHeader = headerFactory.createViaHeader(IP, PORT, "udp", "z9hG4bK");
    		viaHeaders.add(viaHeader);
    		// 由本地设备(Client)生成,全局唯一,每次呼叫这个值唯一不变
    		CallIdHeader callIdHeader;
    		if(CALL_ID == null){
    			callIdHeader = sipProvider.getNewCallId();
    			CALL_ID = callIdHeader.getCallId();
    		}else {
    			callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(CALL_ID);
    		}
    		CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(CSEQ++,
    				Request.REGISTER);
    		// 用于表示这个包最多可以传送多少跳,每经过一跳都会减一当Max-Forwards==0系统会返回483。默认为70
    		MaxForwardsHeader maxForwards = headerFactory
    				.createMaxForwardsHeader(70);
    		// 状态行(请求行)+消息头(请求头)
    		Request request = messageFactory.createRequest(requestURI,
    				Request.REGISTER, callIdHeader, cSeqHeader, fromHeader,
    				toHeader, viaHeaders, maxForwards);
    
    		SipURI contactURI = addressFactory.createSipURI(USERNAME, IP);
    		contactURI.setPort(PORT);
    		Address contactAddress = addressFactory.createAddress(contactURI);
    		contactAddress.setDisplayName(USERNAME);
    		ContactHeader contactHeader = headerFactory
    				.createContactHeader(contactAddress);
    		request.addHeader(contactHeader);
    		ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(3600);
    		request.addHeader(expiresHeader);
    		List<String> agentParam = new ArrayList<String>(1);
    		agentParam.add(USERNAME);
    		UserAgentHeader userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);
    		request.addHeader(userAgentHeader);
    		if(authorizationHeader != null){
    			request.addHeader(authorizationHeader);
    		}
    		System.out.println(request.toString());
    		return request;
    	}
    
  4. 处理401响应

       public void processResponse(ResponseEvent evt) {
    		Response response = evt.getResponse();
    		System.out.println(response.toString());
    		messageProcessor.processInfo(response.toString());
    		int status = response.getStatusCode();
    
    		if ((status >= 200) && (status < 300)) { //Success!
    			messageProcessor.processInfo("Register Success!");
    			System.out.println("注册成功");
    			return;
    		}
    		if(status == 401){
    			// 获取相应中的认证信息
    			WWWAuthenticateHeader wwwAuthenticateHeader = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
    			String realm = wwwAuthenticateHeader.getRealm();
    			String nonce = wwwAuthenticateHeader.getNonce();
    			String algorithm = wwwAuthenticateHeader.getAlgorithm(); // 通常为MD5
    			String qop = wwwAuthenticateHeader.getQop();
    			// 计算Digest响应
    			String ncStr = String.format("%08x", NC_VALUE++).toUpperCase();
    			try {
    			// 处理401根据响应生成认证信息
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
            }
        }
    
  5. 重新发起REGISTER请求

    	String cnonce = Utils.generateCnonce();
        String responseDigest = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(realm,
        USERNAME, PASSWORD,
        nonce, REQUEST_URL, qop, cnonce, ncStr, Request.REGISTER);
        // 构造Authorization头字段
        AuthorizationHeader authorizationHeader = headerFactory.createAuthorizationHeader("Digest");
        authorizationHeader.setUsername(USERNAME);
        authorizationHeader.setRealm(realm);
        authorizationHeader.setNonce(nonce);
        authorizationHeader.setURI(addressFactory.createSipURI(null, SIP_SERVER_IP + ":" + SIP_SERVER_PORT));
        authorizationHeader.setResponse(responseDigest);
        authorizationHeader.setAlgorithm(algorithm);
        authorizationHeader.setCNonce(cnonce);
        authorizationHeader.setNonceCount(Integer.parseInt(ncStr));
        authorizationHeader.setQop(qop);
        Request request = createRequest(authorizationHeader);
        messageProcessor.processInfo(request.toString());
        sipProvider.sendRequest(request);
    
  6. 接收200响应

    if ((status >= 200) && (status < 300)) { //Success!
    			messageProcessor.processInfo("Register Success!");
    			System.out.println("注册成功");
    			return;
    		}
    
  7. 向FreeSWITCH注册

    REGISTER sip:192.168.130.131:5060;transport=udp SIP/2.0
    Call-ID: da961718177def46838dcc0d422cb013@192.168.130.1
    CSeq: 1 REGISTER
    From: "1001" <sip:1001@192.168.130.1>;tag=1001v1.0
    To: "1001" <sip:1001@192.168.130.1>
    Via: SIP/2.0/UDP 192.168.130.1:5061;branch=z9hG4bK
    Max-Forwards: 70
    Contact: "1001" <sip:1001@192.168.130.1:5061>
    Expires: 3600
    User-Agent: 1001
    Content-Length: 0
    
    
    SIP/2.0 401 Unauthorized
    Via: SIP/2.0/UDP 192.168.130.1:5061;branch=z9hG4bK
    From: "1001" <sip:1001@192.168.130.1>;tag=1001v1.0
    To: "1001" <sip:1001@192.168.130.1>;tag=gX8c37XjH661g
    Call-ID: da961718177def46838dcc0d422cb013@192.168.130.1
    CSeq: 1 REGISTER
    User-Agent: FreeSWITCH-mod_sofia/1.10.2-release~64bit
    Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,MESSAGE,INFO,UPDATE,REGISTER,REFER,NOTIFY,PUBLISH,SUBSCRIBE
    Supported: timer,path,replaces
    WWW-Authenticate: Digest realm="192.168.130.1",nonce="b23ac718-7363-4c71-bf2d-cbaa24b75dde",algorithm=MD5,qop="auth"
    Content-Length: 0
    
    
    REGISTER sip:192.168.130.131:5060;transport=udp SIP/2.0
    Call-ID: da961718177def46838dcc0d422cb013@192.168.130.1
    CSeq: 2 REGISTER
    From: "1001" <sip:1001@192.168.130.1>;tag=1001v1.0
    To: "1001" <sip:1001@192.168.130.1>
    Via: SIP/2.0/UDP 192.168.130.1:5061;branch=z9hG4bK
    Max-Forwards: 70
    Contact: "1001" <sip:1001@192.168.130.1:5061>
    Expires: 3600
    User-Agent: 1001
    Authorization: Digest username="1001",realm="192.168.130.1",nonce="b23ac718-7363-4c71-bf2d-cbaa24b75dde",uri="sip:192.168.130.131:5060",response="818dc5bca39bdbaffa8520244be01511",algorithm=MD5,cnonce="zVWY34UveCSzehKlMiLqag==",nc=00000001,qop=auth
    Content-Length: 0
    
    
    SIP/2.0 200 OK
    Via: SIP/2.0/UDP 192.168.130.1:5061;branch=z9hG4bK
    From: "1001" <sip:1001@192.168.130.1>;tag=1001v1.0
    To: "1001" <sip:1001@192.168.130.1>;tag=H61542epeFXmc
    Call-ID: da961718177def46838dcc0d422cb013@192.168.130.1
    CSeq: 2 REGISTER
    Contact: <sip:1001@192.168.130.1:5061>;expires=3600
    Date: Tue, 13 Aug 2024 06:58:45 GMT
    User-Agent: FreeSWITCH-mod_sofia/1.10.2-release~64bit
    Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,MESSAGE,INFO,UPDATE,REGISTER,REFER,NOTIFY,PUBLISH,SUBSCRIBE
    Supported: timer,path,replaces
    Content-Length: 0
    
    
    注册成功
    
    

    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Apple Maps现在可在Firefox和Mac版Edge浏览器中使用

Apple Maps最初只能在 Windows 版 Safari、Chrome 浏览器和 Edge 浏览器上运行&#xff0c;现在已在其他浏览器上运行&#xff0c;包括 Mac 版 Firefox 和 Edge。经过十多年的等待&#xff0c;Apple Maps于今年 7 月推出了新版地图应用的测试版&#xff0c;但只能在有限的浏览器…

哈希表 - 三数之和

15. 三数之和 方法一&#xff1a;排序双指针 /*** param {number[]} nums* return {number[][]}*/ var threeSum function(nums) {const res [], len nums.length;// 将数组排序nums.sort((a, b) > a - b)for (let i 0; i < len; i) {let l i 1, r len - 1, iNum…

什么是令牌桶算法?工作原理是什么?使用它有哪些优点和注意事项?

大家好&#xff0c;我是鸭鸭&#xff01; 此答案节选自鸭鸭最近弄的面试刷题神器面试鸭 &#xff0c;更多大厂常问面试题&#xff0c;可以点击下面的小程序进行阅读哈&#xff01; 目前这个面试刷题小程序刚出&#xff0c;有网页和小程序双端可以使用&#xff01; 回归面试题…

网络初学者必备:100个基础知识全掌握

网络安全学习路线 如果你对网络安全入门感兴趣&#xff0c;那么你需要的话可以点击这里网络安全重磅福利&#xff1a;入门&进阶全套282G学习资源包免费分享&#xff01; 或者扫描下方csdn官方合作二维码获取哦&#xff01; 1 什么是链接? 链接是指两个设备之间的连接。…

OpenCV 基本使用

OpenCV 基本使用 参考教程&#xff1a; GitHub - gaoxiang12/slambook2: edition 2 of the slambook 1. 安装 OpenCV 1.1 下载 OpenCV 参考教程&#xff1a; 无法定位软件包libjasper-dev的解决办法-CSDN博客 视觉slam14讲ch5 opencv安装 ubuntu20.04_libvtk5-dev-CSDN博…

机器学习——聚类算法K-Means

目录 一、初识聚类 1. 认识聚类算法 2. 聚类的流程 3. 簇内误差平方和 Inertia越小模型越好吗&#xff1f; 二、KMeans介绍 1. 重要参数n_clusters 2. 模型评估指标 &#xff08;1&#xff09;真实标签己知的时候 &#xff08;2&#xff09;真实标签未知的时候 三、s…

Tomcat 最大连接数实现原理

spring boot 内置tomcat设置连接数 max-connections: 5 server:port: 9898servlet:context-path: /testtomcat:connection-timeout: 5000max-connections: 5accept-count: 5 ##初始化连接数量connectionLimitLatch protected LimitLatch initializeConnectionLatch() {if (ma…

【区块链+食品安全】农业产业全过程溯源云平台 | FISCO BCOS应用案例

近年来&#xff0c;食品安全问题频发&#xff0c;尤其疫情期间&#xff0c;海鲜、冷冻畜牧产品的入口安全成为大众关注焦点&#xff0c;追溯、确保相关产品生产、运输、售卖等环节的信息真实、有效&#xff0c;成为保证食品安全的核心环节。浙江天演维真网络科技股份有限公司基…

基于ICMP(Ping)的多线程网络通道监视程序(QT)开发

基于ICMP(Ping)的多线程网络通道监视程序(QT)开发 1、 ICMP原理简介 可参考 ICMP(Ping)功能原理及其C实现简介 。 2、 网络通道监视程序开发 设计原理&#xff1a; 通过PING 功能实现服务器、交换机、网闸等设备的网络检测&#xff0c;判断网络的否可达和TTL计算 。 具备功…

微深节能 库区智能化无人天车管理系统 格雷母线

格雷母线在库区智能化无人天车管理系统的应用中&#xff0c;发挥了至关重要的作用。 一、系统构成与原理 系统构成&#xff1a; 格雷母线高精度位移测量系统主要由格雷母线、天线箱、地址解码器、地址编码器四个核心部分组成。这些组件协同工作&#xff0c;实现对移动设备的精确…

c++41两头堵模型

直接拷到自己的内存空间 字符串反转 递归&#xff1a;参数的入栈模型 函数的调用模型 先被调用的模型后执行 向局部变量拷贝内存 传两个参数 做函数结果

【解析几何笔记】1.课程介绍与要求

听的是B站UP主唐小谦的解析几何课&#xff0c;万年的老计算机专业人也想学数学OWO。 1. 课程介绍与要求 前面都是老师的课程介绍&#xff0c;从板书证明开始记 【证明】在三角形ABC中&#xff0c;D为BC边的中点&#xff0c;证明&#xff1a; ∠ ABC 9 0 ∘ \angle \text{ABC…

出现 AWS ECS 错误:集群中未找到容器实例处理办法?

使用部署docker容器映像&#xff0c;但未创建 EC2 实例的情况下。出现下面错误提示。 “调用 RunTask 操作时发生客户端错误 (InvalidParameterException)&#xff1a;在您的集群中未找到容器实例。” 经过以下的步骤&#xff1a; 1.将 Docker 映像从 Ubuntu 推送到我的 Amazo…

c++ - 特殊类设计

文章目录 一、设计一个不允许拷贝的类二、设计一个只能在堆上实例对象的类三、设计一个只能在栈上创建对象的类四、设计一个不能被继承的类五、设计一个只能创建一个对象的类&#xff08;单例模式&#xff09; 一、设计一个不允许拷贝的类 1、方法一&#xff1a;将拷贝构造和赋…

在Windows上配置VSCode MinGW+CMake(包括C++多线程编程的两套API:posix和win32)

创建目录 首先&#xff0c;需要电脑上安装VSCode, 并且创建三个文件夹&#xff1a;cmake、MinGW-posix、MinGW-w32 文件下载 下载posix-seh posix和win32分别是c多线程变成的两套API,可根据不同需求安装&#xff0c;现在先下载配置环境需要的几个文件 百度搜索MinGW-64 点…

Django 链接MySQL数据库,报错Did you install mysqlclient?

据说是在python3里面&#xff0c;已经用pymysql替换了MySQLdb来进行数据库连接 所以&#xff0c;先安装pymysql pip install pymysql之后为了测试这个问题是否和mysql-connector-c有关系&#xff0c;我提前把这个应用给卸了。 之后在整个项目根目录的init文件内写入以下内容 im…

在docker中进行日志切割

先在Linux中安装docker&#xff0c;然后在docker中安装appnode面板&#xff0c;并进行docker网络端口映射。接着进入docker&#xff0c;进行nginx日志切割。 安装docker 第一步&#xff0c;卸载旧版本docker。 若系统中已安装旧版本docker&#xff0c;则需要卸载旧版本docke…

大模型在资源全生命周期的应用探索

资源全生命周期管理的传统价值 运营商的网络涉及接入网、数据网、承载网、核心网、传输网、无线网、光缆网、云专网、动力网、业务平台等十数类大专业。网络资源的全生命周期体现在以下六大生产活动环节&#xff1a;网络规划→网络设计→网络工程建设→网络资源的投入使用→网络…

数据安全防护措施有哪些?防数据泄露的10大措施丨让你一次性看够!

古时烽火传信&#xff0c;密语藏于竹简之间&#xff0c;以防外泄&#xff0c;保家国安宁。 今朝数字洪流&#xff0c;数据如织&#xff0c;信息安全之重&#xff0c;不亚于昔日之密信。 在信息爆炸的时代&#xff0c;数据安全防护犹如筑起铜墙铁壁&#xff0c;以防数据泄露之患…

想要数字人直播平台赚钱,前期源码部署要注意哪些要点?

随着人工智能时代的到来&#xff0c;数字人直播的应用频率不断升高&#xff0c;展现巨大收益潜力的同时&#xff0c;也让不少想要通过数字人源码厂商搭建数字人直播平台的创业者产生好奇&#xff0c;并开始从各方面打听数字人直播平台怎么赚钱等相关问题的答案。 本期&#xf…