采用JAIN SIP API实现一个SIP客户端实现向SIP服务器注册。SIP服务器可以为FreeSWITCH也可以为满足GB28181的SIP平台。话不多说直接看注册流程图:
代码实现:
-
创建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>
-
构造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); }
-
发起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; }
-
处理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(); } } }
-
重新发起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);
-
接收200响应
if ((status >= 200) && (status < 300)) { //Success! messageProcessor.processInfo("Register Success!"); System.out.println("注册成功"); return; }
-
向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 注册成功