织梦CMS - 轻松建站从此开始!

罗索

基于JAINSIP协议栈的一个简单SIP服务器实现流程

jackyhwei 发布于 2011-11-28 21:41 点击:次 
SIP服务器采用B2BUA,sip呼叫控制实现流程
TAG:

SIP服务器采用B2BUA,sip呼叫控制实现流程: 

INVITE的请求: 
收到INVITE后,克隆INVITE消息。替换Request URI被叫注册的地址。 
替换VIA头为服务器地址。通过该INVITE请求得到客户端事务并通过 
该事务来发送INVITE请求。 

180,200 OK响应的处理: 
根据返回的响应码创建新的响应。 
替换CONTACT头地址为服务器的地址。 
拷贝原响应中的媒体内容至新的响应中。 
通过INVITE中保存下来的服务端事务发送响应消息。 

ACK的处理: 
从ACK请求中得到序列数。 
通过被叫对话来创建新的ACK请求。 
通过被叫对话发送ACK请求。 

BYE的处理流程: 
根据被叫端的客户端事务得到发送给被叫的INVITE请求,根据INVITE请求创建BYE消息。 
BYE响应消息处理流程: 
根据DIALOG得到服务端事务,并由此得到BYE请求。根据BYE请求创建响应。 

CANCEL的处理流程: 
向主叫响应CANCEL 200 OK。 
根据客户端事务得到发送给主叫的INVITE请求中的URI,CALLID,FROM,TO,VIA头域来创建CANCEL请求。 

注:非200 OK的ACK响应是由协议栈来实现的,不需要应用层来发送。也就是说,ACK消息不会经过应用层。 

package com.voip.sipphone; 
import gov.nist.javax.sip.address.SipUri; 
import gov.nist.javax.sip.header.CSeq; 
import gov.nist.javax.sip.header.Contact; 
import gov.nist.javax.sip.header.ContentLength; 
import gov.nist.javax.sip.header.ContentType; 
import gov.nist.javax.sip.header.From; 
import gov.nist.javax.sip.header.Via; 
import java.text.ParseException; 
import java.util.ArrayList; 
import java.util.Hashtable; 
import java.util.List; 
import java.util.Properties; 
import java.util.Timer; 
import javax.sip.*; 
import javax.sip.address.Address; 
import javax.sip.address.AddressFactory; 
import javax.sip.address.URI; 
import javax.sip.header.CallIdHeader; 
import javax.sip.header.ContactHeader; 
import javax.sip.header.ExpiresHeader; 
import javax.sip.header.FromHeader; 
import javax.sip.header.Header; 
import javax.sip.header.HeaderFactory; 
import javax.sip.header.MaxForwardsHeader; 
import javax.sip.header.RecordRouteHeader; 
import javax.sip.header.ToHeader; 
import javax.sip.header.ViaHeader; 
import javax.sip.message.MessageFactory; 
import javax.sip.message.Request; 
import javax.sip.message.Response; 
public class SipPhone implements SipListener  
{ 
 public void processDialogTerminated(DialogTerminatedEvent arg0) { 
  // TODO Auto-generated method stub 
  System.out.println("processDialogTerminated " + arg0.toString()); 
 } 
 public void processIOException(IOExceptionEvent arg0) { 
  // TODO Auto-generated method stub 
  System.out.println("processIOException " + arg0.toString()); 
 } 
  
 /** 
  * 保存当前注册的用户 
  */ 
 private static Hashtable<URI, URI> currUser = new Hashtable(); 
  
  
 /** 
  * @author software 
  * 注册定时器 
  */ 
 class TimerTask extends Timer 
 { 
  /** 
   * default constructor 
   */ 
  public TimerTask() 
  { 
    
  } 
   
  /** 
   *  如果定时任务到,则删除该用户的注册信息 
   */ 
  public void run() 
  { 
    
  } 
 } 
  
  
 /** 
  * 服务器侦听IP地址 
  */ 
 private String ipAddr = "192.168.0.20"; 
  
  
 /** 
  * 服务器侦听端口 
  */ 
 private int port = 5060; 
  
 /** 
  * 处理register请求 
  * @param request 请求消息 
  */ 
 private void processRegister(Request request, RequestEvent requestEvent) 
 { 
  if (null == request) 
  { 
   System.out.println("processInvite request is null."); 
   return; 
  } 
  //System.out.println("Request " + request.toString()); 
  ServerTransaction serverTransactionId = requestEvent.getServerTransaction(); 
   
  try  
  { 
   Response response = null; 
   ToHeader head = (ToHeader)request.getHeader(ToHeader.NAME); 
   Address toAddress = head.getAddress(); 
   URI toURI = toAddress.getURI(); 
   ContactHeader contactHeader = (ContactHeader) request.getHeader("Contact"); 
   Address contactAddr = contactHeader.getAddress(); 
   URI contactURI = contactAddr.getURI(); 
   System.out.println("processRegister from: " + toURI + " request str: " + contactURI); 
   int expires = request.getExpires().getExpires(); 
   // 如果expires不等于0,则为注册,否则为注销。 
   if (expires != 0 || contactHeader.getExpires() != 0) 
   { 
    currUser.put(toURI, contactURI); 
    System.out.println("register user " + toURI); 
   } 
   else 
   {   
    currUser.remove(toURI); 
    System.out.println("unregister user " + toURI); 
   } 
    
   response = msgFactory.createResponse(200, request); 
   System.out.println("send register response  : " + response.toString());    
    
   if(serverTransactionId == null) 
   { 
    serverTransactionId = sipProvider.getNewServerTransaction(request); 
    serverTransactionId.sendResponse(response); 
    //serverTransactionId.terminate(); 
    System.out.println("register serverTransaction: " + serverTransactionId); 
   } 
   else 
   { 
    System.out.println("processRequest serverTransactionId is null."); 
   } 
    
  } 
  catch (ParseException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (SipException e)  
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (InvalidArgumentException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
 } 
  
 /** 
  * 处理invite请求 
  * @param request 请求消息 
  */ 
 private void processInvite(Request request, RequestEvent requestEvent) 
 { 
  if (null == request) 
  { 
   System.out.println("processInvite request is null."); 
   return; 
  } 
  try 
  {  
   // 发送100 Trying 
   serverTransactionId = requestEvent.getServerTransaction(); 
   if (serverTransactionId == null) 
   { 
    serverTransactionId = sipProvider.getNewServerTransaction(request); 
    callerDialog = serverTransactionId.getDialog(); 
    Response response = msgFactory.createResponse(Response.TRYING, request); 
    serverTransactionId.sendResponse(response); 
   } 
   //查询目标地址 
   URI reqUri = request.getRequestURI(); 
   URI contactURI = currUser.get(reqUri); 
    
   System.out.println("processInvite rqStr=" + reqUri + " contact=" + contactURI); 
    
   //根据Request uri来路由,后续的响应消息通过VIA来路由 
   Request cliReq = msgFactory.createRequest(request.toString()); 
   cliReq.setRequestURI(contactURI); 
       
   Via callerVia = (Via)request.getHeader(Via.NAME); 
   Via via = (Via) headerFactory.createViaHeader(ipAddr, port, "UDP", callerVia.getBranch()+"sipphone"); 
    
   // FIXME 需要测试是否能够通过设置VIA头域来修改VIA头域值 
   cliReq.removeHeader(Via.NAME); 
   cliReq.addHeader(via); 
    
   // 更新contact的地址 
   ContactHeader contactHeader = headerFactory.createContactHeader(); 
   Address address = addressFactory.createAddress("sip:sipsoft@" + ipAddr +":"+ port); 
   contactHeader.setAddress(address); 
   contactHeader.setExpires(3600); 
   cliReq.setHeader(contactHeader); 
    
   clientTransactionId = sipProvider.getNewClientTransaction(cliReq); 
   clientTransactionId.sendRequest(); 
    
   System.out.println("processInvite clientTransactionId=" + clientTransactionId.toString()); 
    
   System.out.println("send invite to callee: " + cliReq);       
  } 
  catch (TransactionUnavailableException e1) 
  { 
   // TODO Auto-generated catch block 
   e1.printStackTrace(); 
  } 
  catch (SipException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (ParseException e) 
  { 
   e.printStackTrace(); 
  } 
  catch (Exception e) 
  { 
   e.printStackTrace(); 
  } 
 } 
  
 /** 
  * 处理SUBSCRIBE请求 
  * @param request 请求消息 
  */ 
 private void processSubscribe(Request request) 
 { 
  if (null == request) 
  { 
   System.out.println("processSubscribe request is null."); 
   return; 
  } 
  ServerTransaction serverTransactionId = null; 
  try  
  { 
   serverTransactionId = sipProvider.getNewServerTransaction(request); 
  } 
  catch (TransactionAlreadyExistsException e1) 
  { 
   // TODO Auto-generated catch block 
   e1.printStackTrace(); 
  } catch (TransactionUnavailableException e1) 
  { 
   // TODO Auto-generated catch block 
   e1.printStackTrace(); 
  } 
   
  try  
  { 
   Response response = null; 
   response = msgFactory.createResponse(200, request); 
   if (response != null) 
   { 
    ExpiresHeader expireHeader = headerFactory.createExpiresHeader(30); 
    response.setExpires(expireHeader); 
   } 
   System.out.println("response : " + response.toString()); 
    
   if(serverTransactionId != null) 
   { 
    serverTransactionId.sendResponse(response); 
    serverTransactionId.terminate();      
   } 
   else 
   { 
    System.out.println("processRequest serverTransactionId is null."); 
   } 
    
  } 
  catch (ParseException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (SipException e)  
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (InvalidArgumentException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
 } 
 /** 
  * 处理BYE请求 
  * @param request 请求消息 
  */ 
 private void processBye(Request request, RequestEvent requestEvent) 
 { 
  if (null == request || null == requestEvent) 
  { 
   System.out.println("processBye request is null."); 
   return; 
  } 
  Request byeReq = null; 
  Dialog dialog = requestEvent.getDialog(); 
  System.out.println("calleeDialog : " + calleeDialog); 
  System.out.println("callerDialog : " + callerDialog); 
  try 
  { 
   if (dialog.equals(calleeDialog)) 
   { 
    byeReq = callerDialog.createRequest(request.getMethod()); 
    ClientTransaction clientTran = sipProvider.getNewClientTransaction(byeReq); 
    callerDialog.sendRequest(clientTran); 
    calleeDialog.setApplicationData(requestEvent.getServerTransaction()); 
   } 
   else if (dialog.equals(callerDialog)) 
   { 
    byeReq = calleeDialog.createRequest(request.getMethod()); 
    ClientTransaction clientTran = sipProvider.getNewClientTransaction(byeReq); 
    calleeDialog.sendRequest(clientTran);  
    callerDialog.setApplicationData(requestEvent.getServerTransaction()); 
   } 
   else 
   { 
    System.out.println(""); 
   } 
    
   System.out.println("send bye to peer:" + byeReq.toString()); 
  } 
  catch (SipException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  }     
   
 } 
  
 /** 
  * 处理CANCEL请求 
  * @param request 请求消息 
  */ 
 private void processCancel(Request request) 
 { 
  if (null == request) 
  { 
   System.out.println("processCancel request is null."); 
   return; 
  } 
 } 
  
 /** 
  * 处理INFO请求 
  * @param request 请求消息 
  */ 
 private void processInfo(Request request) 
 { 
  if (null == request) 
  { 
   System.out.println("processInfo request is null."); 
   return; 
  } 
 } 
  
  
 /** 
  * 处理ACK请求 
  * @param request 请求消息 
  */ 
 private void processAck(Request request, RequestEvent requestEvent) 
 { 
  if (null == request) 
  { 
   System.out.println("processAck request is null."); 
   return; 
  } 
   
  try  
  { 
   Request ackRequest = null; 
   CSeq csReq = (CSeq)request.getHeader(CSeq.NAME);    
   ackRequest = calleeDialog.createAck(csReq.getSeqNumber()); 
   calleeDialog.sendAck(ackRequest);    
   System.out.println("send ack to callee:" + ackRequest.toString()); 
  } 
  catch (SipException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  }  
  catch (InvalidArgumentException e)  
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
   
   
 } 
  
 /** 
  * 处理CANCEL消息 
  * @param request 
  * @param requestEvent 
  */ 
 private void processCancel(Request request, RequestEvent requestEvent) 
 { 
  // 判断参数是否有效 
  if (request == null || requestEvent == null) 
  { 
   System.out.println("processCancel input parameter invalid."); 
   return; 
  } 
     
  try 
  { 
   // 发送CANCEL 200 OK消息 
   Response response = msgFactory.createResponse(Response.OK, request); 
   ServerTransaction cancelServTran = requestEvent.getServerTransaction(); 
   if (cancelServTran == null) 
   { 
    cancelServTran = sipProvider.getNewServerTransaction(request); 
   } 
   cancelServTran.sendResponse(response); 
    
   // 向对端发送CANCEL消息    
   Request cancelReq = null; 
   Request inviteReq = clientTransactionId.getRequest(); 
   List list = new ArrayList(); 
   Via viaHeader = (Via)inviteReq.getHeader(Via.NAME); 
   list.add(viaHeader); 
    
   CSeq cseq = (CSeq)inviteReq.getHeader(CSeq.NAME); 
   CSeq cancelCSeq = (CSeq)headerFactory.createCSeqHeader(cseq.getSeqNumber(), Request.CANCEL); 
   cancelReq = msgFactory.createRequest(inviteReq.getRequestURI(), 
     inviteReq.getMethod(), 
     (CallIdHeader)inviteReq.getHeader(CallIdHeader.NAME), 
     cancelCSeq, 
     (FromHeader)inviteReq.getHeader(From.NAME), 
     (ToHeader)inviteReq.getHeader(ToHeader.NAME), 
     list,  
     (MaxForwardsHeader)inviteReq.getHeader(MaxForwardsHeader.NAME)); 
   ClientTransaction cancelClientTran = sipProvider.getNewClientTransaction(cancelReq); 
   cancelClientTran.sendRequest(); 
  } 
  catch (ParseException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  }  
  catch (TransactionAlreadyExistsException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (TransactionUnavailableException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  }  
  catch (SipException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (InvalidArgumentException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
 } 
  
  
 private ServerTransaction serverTransactionId = null; 
 /* (non-Javadoc) 
  * @see javax.sip.SipListener#processRequest(javax.sip.RequestEvent) 
  */ 
 public void processRequest(RequestEvent arg0)  
 { 
  Request request = arg0.getRequest(); 
  if (null == request) 
  { 
   System.out.println("processRequest request is null."); 
   return; 
  } 
  System.out.println("processRequest:" + request.toString()); 
  if (Request.INVITE.equals(request.getMethod())) 
  {       
   processInvite(request, arg0); 
  } 
  else if (Request.REGISTER.equals(request.getMethod())) 
  { 
   processRegister(request, arg0); 
  } 
  else if (Request.SUBSCRIBE.equals(request.getMethod())) 
  { 
   processSubscribe(request); 
  } 
  else if (Request.ACK.equalsIgnoreCase(request.getMethod())) 
  { 
   processAck(request, arg0); 
  } 
  else if (Request.BYE.equalsIgnoreCase(request.getMethod())) 
  { 
   processBye(request, arg0); 
  } 
  else if (Request.CANCEL.equalsIgnoreCase(request.getMethod())) 
  { 
   processCancel(request, arg0); 
  } 
  else 
  { 
   System.out.println("no support the method!"); 
  } 
 } 

 /** 
  * 主叫对话 
  */ 
 private Dialog calleeDialog = null; 
  
 /** 
  * 被叫对话 
  */ 
 private Dialog callerDialog = null; 
  
 /** 
  *  
  */ 
 ClientTransaction clientTransactionId = null; 
  
 /** 
  * 处理BYE响应消息 
  * @param reponseEvent 
  */ 
 private void doByeResponse(Response response, ResponseEvent responseEvent) 
 { 
  Dialog dialog = responseEvent.getDialog(); 
   
  try 
  { 
   Response byeResp = null; 
   if (callerDialog.equals(dialog)) 
   { 
    ServerTransaction servTran = (ServerTransaction)calleeDialog.getApplicationData(); 
    byeResp = msgFactory.createResponse(response.getStatusCode(), servTran.getRequest()); 
    servTran.sendResponse(byeResp); 
   } 
   else if (calleeDialog.equals(dialog)) 
   { 
    ServerTransaction servTran = (ServerTransaction)callerDialog.getApplicationData(); 
    byeResp = msgFactory.createResponse(response.getStatusCode(), servTran.getRequest()); 
    servTran.sendResponse(byeResp); 
   } 
   else 
   { 
     
   } 
   System.out.println("send bye response to peer:" + byeResp.toString()); 
  } 
  catch (ParseException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } catch (SipException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } catch (InvalidArgumentException e)  
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
 } 
 /* (non-Javadoc) 
  * @see javax.sip.SipListener#processResponse(javax.sip.ResponseEvent) 
  *  
  */ 
 public void processResponse(ResponseEvent arg0) 
 { 
  // FIXME 需要判断各个响应对应的是什么请求 
  Response response = arg0.getResponse(); 
   
  System.out.println("recv the response :" +  response.toString()); 
  System.out.println("respone to request : " + arg0.getClientTransaction().getRequest()); 
   
  if (response.getStatusCode() == Response.TRYING) 
  { 
   System.out.println("The response is 100 response."); 
   return; 
  } 
   
  try 
  { 
   ClientTransaction clientTran = (ClientTransaction) arg0.getClientTransaction(); 
    
   if (Request.INVITE.equalsIgnoreCase(clientTran.getRequest().getMethod())) 
   { 
    int statusCode = response.getStatusCode();    
    Response callerResp = null; 
  
    callerResp = msgFactory.createResponse(statusCode, serverTransactionId.getRequest()); 
  
    // 更新contact头域值,因为后面的消息是根据该URI来路由的 
    ContactHeader contactHeader = headerFactory.createContactHeader(); 
    Address address = addressFactory.createAddress("sip:sipsoft@"+ipAddr+":"+port);    
    contactHeader.setAddress(address); 
    contactHeader.setExpires(3600); 
    callerResp.addHeader(contactHeader); 
     
    // 拷贝to头域 
    ToHeader toHeader = (ToHeader)response.getHeader(ToHeader.NAME); 
    callerResp.setHeader(toHeader); 
     
    // 拷贝相应的消息体 
    ContentLength contentLen = (ContentLength)response.getContentLength(); 
    if (contentLen != null && contentLen.getContentLength() != 0) 
    { 
     ContentType contentType = (ContentType)response.getHeader(ContentType.NAME); 
     System.out.println("the sdp contenttype is " + contentType); 
      
     callerResp.setContentLength(contentLen); 
     //callerResp.addHeader(contentType); 
     callerResp.setContent(response.getContent(), contentType); 
    } 
    else 
    { 
     System.out.println("sdp is null."); 
    } 
    if (serverTransactionId != null) 
    { 
     callerDialog = serverTransactionId.getDialog(); 
     calleeDialog = clientTran.getDialog(); 
     serverTransactionId.sendResponse(callerResp); 
     System.out.println("callerDialog=" + callerDialog); 
     System.out.println("serverTransactionId.branch=" + serverTransactionId.getBranchId());      
    } 
    else 
    { 
     System.out.println("serverTransactionId is null."); 
    }    
     
    System.out.println("send response to caller : " + callerResp.toString()); 
   } 
   else if (Request.BYE.equalsIgnoreCase(clientTran.getRequest().getMethod())) 
   { 
    doByeResponse(response, arg0); 
   } 
   else if (Request.CANCEL.equalsIgnoreCase(clientTran.getRequest().getMethod())) 
   { 
    //doCancelResponse(response, arg0); 
   } 
   else  
   { 
     
   } 
    
    
  } 
  catch (ParseException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (SipException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (InvalidArgumentException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
  catch (Exception ex) 
  { 
   ex.printStackTrace(); 
  } 
   
 } 
  
 private void doCancelResponse(Response response, ResponseEvent responseEvent) 
 { 
  //FIXME  需要验证参数的有效性 
  ServerTransaction servTran = (ServerTransaction)callerDialog.getApplicationData(); 
  Response cancelResp; 
  try  
  { 
   cancelResp = msgFactory.createResponse(response.getStatusCode(), servTran.getRequest()); 
   servTran.sendResponse(cancelResp); 
  } 
  catch (ParseException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  }  
  catch (SipException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  }  
  catch (InvalidArgumentException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
   
 } 
 public void processTimeout(TimeoutEvent arg0) 
 { 
  // TODO Auto-generated method stub 
  System.out.println(" processTimeout " + arg0.toString()); 
 } 
 public void processTransactionTerminated(TransactionTerminatedEvent arg0) { 
  // TODO Auto-generated method stub 
  System.out.println(" processTransactionTerminated " + arg0.getClientTransaction().getBranchId()  
        + " " + arg0.getServerTransaction().getBranchId());  
 } 
  
 private static SipStack sipStack = null; 
  
 private static AddressFactory addressFactory = null; 
  
 private static MessageFactory msgFactory = null; 
  
 private static HeaderFactory headerFactory = null; 
  
 private static SipProvider sipProvider = null; 
  
 private void init() 
 { 
  SipFactory sipFactory = null; 
   
  sipFactory = SipFactory.getInstance(); 
  if (null == sipFactory) 
  { 
   System.out.println("init sipFactory is null."); 
   return; 
  } 
   
  sipFactory.setPathName("gov.nist"); 
  Properties properties = new Properties(); 
  properties.setProperty("javax.sip.STACK_NAME", "sipphone"); 
  // You need 16 for logging traces. 32 for debug + traces. 
  // Your code will limp at 32 but it is best for debugging. 
  properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32"); 
  properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", 
    "sipphonedebug.txt"); 
  properties.setProperty("gov.nist.javax.sip.SERVER_LOG", 
    "sipphonelog.txt"); 
  try  
  { 
   sipStack = sipFactory.createSipStack(properties); 
  } 
  catch (PeerUnavailableException e) 
  { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
   return; 
  } 
   
  try 
  { 
   headerFactory = sipFactory.createHeaderFactory(); 
   addressFactory = sipFactory.createAddressFactory(); 
   msgFactory = sipFactory.createMessageFactory(); 
   ListeningPoint lp = sipStack.createListeningPoint("192.168.0.20", 
     5060, "udp"); 
   SipPhone listener = this; 
    
   sipProvider = sipStack.createSipProvider(lp); 
   System.out.println("udp provider " + sipProvider.toString()); 
   sipProvider.addSipListener(listener); 
  } 
  catch (Exception ex) 
  { 
   ex.printStackTrace(); 
   return; 
  } 
   
 } 
  
 /** 
  * 程序入口 
  * @param args 
  */ 
 public static void main(String []args) 
 { 
  new SipPhone().init(); 
 } 
  
}

 

(匿名)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201111/15373.html]
本文出处:网络 作者:匿名
顶一下
(2)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容