[LCS]深入了解SipSnoop—事件篇

write by PanQi@Ultrapower 

摘要:SipSnoop用于监听Response和Request的SIP消息,是LCS2005自带的一款非常酷的应用程序,虽不及Ethereal强大,但也足以满足一般监听,本文将对其核心方法进行分析。



l        
知识点:
       [MSPL]内置变量sipRequestsipRequest变量包含了当前从Live Communications Server到应用程序的SIP请求消息。
       [MSPL]内置变量sipResponsesipResponse变量包含了当前从Live Communications Server到应用程序的SIP响应消息。
       [MSPL] Dispatch方法:Dispatch方法为应用程序的内部的事件处理者(event handler)分配一个事件。如果成功转发到指定的方法则返回真,否则返回否。
      
       [Microsoft.Rtc.Sip]RequestReceivedEventArgsRequestReceivedEventArgs类定义应用程序上关于到达的SIP请求信息。当一个请求成功的被MSPL消息过滤器分配时,一个包含RequestReceivedEventArgs对象签名的事件将被分配到指定的方法。
 
 
l         深入了解SipSnoop
1          提出问题:
1.1         判断消息类型,针对不同消息类型分配不同的事件供给应用程序处理。
1.2         应用程序截获并处理通过LCS的全部ResponseRequest消息。
2          分析问题:
2.1         判断SIP消息类型是通过MSPL的内置变量sipRequestsipResponse来完成。然后用MSPLDispatch方法分配不同的事件给托管代码来进行下一步处理。
2.2         托管代码应用程序在处理MSPL分配的事件里实例化Response类和Resquest类,通过这两个对象可获取通过LCS的全部请求、响应消息。
3          解决方案:
SIP共有六种请求方法,分别是:INVITE(邀请);ACK(确认);OPTIONS(可选项);BYE(再见);CANCEL(取消);REGISTER(注册);SipSnoop的核心事件RequestHandlerResponseHandler中,结合哈希表统计了通过LCSSIP请求数量。下面针对这两个事件进行详细分析。
3.1         截获并统计Request消息:
3.1.1   MSPL分配的事件,触发RequestHandler事件处理者,该事件处理者有一个RequestReceivedEventArgs对象签名,通过这个对象签名的Request属性截获当前通过LCSRequest消息。代码如下:
public void RequestHandler(object sender, RequestReceivedEventArgs e)
{
    Request request 
= e.Request;


3.1.2   获取到通过LCSSIP请求消息后进行请求方式的判断,针对不同请求方式进行不同的处理,目的就是统计计数;对Request消息,针对INVITEACK进行了单独处理计数,其它方式采用Other方法计数。
如果是INVITE的请求方式,说明该会话是首次建立,需要新建一个会话(Session)实例,并将会话的States(状态)属性赋值为Initializing,接下来,锁定哈希表sessionStateTableSyncRoot属性,确保线程安全,然后为哈希表sessionStateTablecallIdHeader.Value赋值。代码如下:

if (request.StandardMethod == Request.StandardMethodType.Invite)
{      
 
///extract the call-id and create session state
Header callIdHeader = request.AllHeaders.FindFirst("Call-ID");
 
    
if (callIdHeader != null)
    
{
        Session newSession 
= new Session();
        newSession.State 
= Session.States.Initializing;
                                          
        
lock (sessionStateTable.SyncRoot)
        
{
            sessionStateTable[callIdHeader.Value] 
= newSession;
        }

    }

}
 


如果请求方式为
ACK,说明是确认请求,在报头中获取Call-ID,依靠得到的Call-ID,在哈希表sessionStateTable中寻找匹配的session,然后进行装箱转换成Session对象,接下来针对Session对象进行判断,如果该对象不为空的话会话状态赋值为Established,说明会话连接已确定,执行Update方法,以原子操作的形式递增指定变量totalSessionsactiveSessions的值并存储结果。代码如下:
else if (request.StandardMethod == Request.StandardMethodType.Ack)
{
 
                                   
///extract the call-id and update session state, ignore errors
                                   Header callIdHeader = request.AllHeaders.FindFirst("Call-ID");
 
                                   
if (callIdHeader != null)
                                   
{
                                          Session session 
= sessionStateTable[callIdHeader.Value] as Session;
                                          
if (session != null)
                                          
{
                                                 session.State 
= Session.States.Established;
                                                 statistics.Update(
true /* new session */);
                                          }

       }

}


Update
方法中递增指定变量的代码如下:

public void Update(bool sessionEstablished)
                            
{
                                   
if (sessionEstablished)
                                   
{
                                          Interlocked.Increment(
ref totalSessions);
                                          Interlocked.Increment(
ref activeSessions);
                                   }

                                   
else
                                   
{
                                          Interlocked.Decrement(
ref activeSessions);
                                   }

}


如果在
Request消息中有INVITEACK之外的请求方式,均采用一种方法计数,实现代码如下:
statistics.Update(request.StandardMethod);
 
3.1.3   获取完请求方式后,获取请求消息报头中的发送方和接受方,将其Update到哈希表userTable中,代码如下:
Header fromHeader = request.AllHeaders.FindFirst("From"); 
Header toHeader 
= request.AllHeaders.FindFirst("To"); 
  
statistics.Update(UriParser.GetUserAtHost(fromHeader.Value)); 
statistics.Update(UriParser.GetUserAtHost(toHeader.Value)); 
Update方法:
public void Update(string user) 

    
if  (user != null
    

        
lock (userTable.SyncRoot) 
        

            userTable[user] 
= user; 
        }
 
    }
 
}
 



至此,实现
Request消息的获取及统计功能。
 
3.2         截获并统计Response消息:
SipSnoopResponse消息统计中针对请求方式Bye进行HashTableRemove操作,其它请求方式不采用哈希表,只计数;代码实现过程与Request类似,不再赘述。
 
 
 
write by PanQi@Ultrapower 
2005-6-9
posted @ 2005-06-09 19:09 Frank Pan 阅读(2813) 评论(10) 编辑 收藏

 回复 引用 查看   
#1楼 2005-06-10 09:00 James      
1 提出问题:
1.1 判断消息类型,针对不同消息类型分配不同的事件供给应用程序处理。
1.2 应用程序截获并处理通过LCS的全部Response、Request消息。

感覺和我前段時間做的一個東西比較像.
你能重點分析一下其設計麼?

 回复 引用 查看   
#2楼[楼主] 2005-06-10 09:08 Richer      
计划下一篇写针对SipSnoop设计及委托机制方面的随笔。
 回复 引用   
#3楼 2005-06-10 09:38 CsOver
支持,好理解!
 回复 引用   
#4楼 2005-06-11 17:19 csyw
LCS 的源码在哪里可以下载到?望告之,谢谢!
 回复 引用 查看   
#5楼[楼主] 2005-06-13 09:11 Richer      
LCS的SDK里有。
 回复 引用   
#6楼 2005-06-13 18:57 csyw
Richer ,那我怎样能取得你上面例子中的源码,是你自己写的,还是LCS里自带的例子,我想要一份来研究,能否发给我一份,lucky_zqlg@163.com

谢谢!

 回复 引用 查看   
#7楼[楼主] 2005-06-14 09:10 Richer      
已经送出,在LCS自带的SDK中。
 回复 引用   
#8楼 2007-03-07 15:54 千山暮雪[未注册用户]
非常不错,正在研究LCS开发,希望以后能多多指点!
推荐一个免费的多语种在线翻译网站www.165net.com,可进行十多种语言的互译:汉语、英语、日语、德语、法语、俄语、韩语、意大利语、西班牙语、葡萄牙语、希腊语和荷兰语等。同时该网站还提供人工翻译服务,在全国各大城市都有办事处。
机械电子翻译公司
钢铁冶金翻译公司

 回复 引用 查看   
#10楼 2008-12-03 22:55 2℃空间      
楼主,您好!我想把监听到的客户端的请求进行加工后再发给server,应该怎么做呢?
如何把加工后的信息发给server啊?