天天看点

sipp脚本撰写(二)

1.1脚本撰写

进行脚本撰写之前需要熟悉一些默认的参数关键字:

关键词 默认值 说明
【service】 service 由参数-s传递,一般用来指定单个主被叫
[remote_ip] 远端设备地址
[remote_port] 远端设备端口。可以在脚本中使用偏移量,如[remote_port+3]
[local_ip] 本地ip 可以由参数-i指定
[local_ip_type] ip版本 远端设备地址
[local_port] 系统随机分配 可由-p指定,可以在脚本中使用偏移量,如[local_port+3]
[len] sdp长度,用于”Content-Length”头域,由sipp自动生成或者手动指定,可以添加偏移量,如[len+3]
[call_number] 呼叫索引,从1开始,每增加一个呼叫递增1
[cseq] 初始值为1,可以使用参数 -base_cseq手动指定初始值。
[media_ip] 本地媒体流ip,可以由-mi参数指定
[media_ip_type] 本地媒体流ip版本
[media_port] 本地媒体流端口,可由-mp指定,可以设置偏移量[media_port+3]
[last_*] 此关键词用于从接收的上一个sip消息中提取指定头域(如果存在)的值。比如[last_to]则表示从接收的上一个sip消息中提取To域的消息保存到[last_to]中并应用。
[branch] 生成一个由(z9hG4bK) + call number + message索引组成的branch id到脚本中。
[field0-n file=

<filename>

line=

<number>

]
从外部文件csv加载值,file表示选择从命令行中指定的csv文件的一个文件作为外部文件,line定义选择的外部文件的起始行,field选择字段。

脚本参数化:

1、需要sipp命令赋值的参数-p、-i、-s:[local_ip]、[local_port]、[remote_ip]、[remote_port]、[service]

2、sipp自动检测生成的参数:[call_number]、[call_id]、[cseq]、[len]、[branch]

创建uac

<send retrans="500">
<![CDATA[
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port]
From: sipp <sip:[email protected][local_ip]:[local_port]>;tag=[call_number]
To: sut <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
Cseq: 1 INVITE
Contact: sip:[email protected][local_ip]:[local_port]
Max-Forwards: 70
Content-Type: application/sdp
Content-Length: [len]

v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
s=-
t=0 0
c=IN IP[media_ip_type] [media_ip]
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
           

在send命令内部,必须将待发送的sip消息括入

"<![CDATA" 和 "]]>"

中间,在这中间的所有内容将会被发送到远端系统。同时,在这个示例中包含一些特殊的关键词,比如:[service],[remote_ip],这些关键词可以通过sipp命令参数来进行赋值,具体可见上表。另外:加入retrans参数,可在没收到响应的情况下,在设定的时间之后重传,此例中为500毫秒。

注意:在测试业务时,通过在头域中添加

<Route:

被叫侧地址>以保证呼叫会从业务路由到被叫,否则路由会通过Request URI确定下一跳地址。

SIPp脚本中可以使用”recv”命令等待接收消息。如下:

<recv response="100" optional="true">
</recv>

<recv response="180" optional="true">
</recv>

<recv response="200">
</recv>
           

其中,100和180消息是可选接收的(optional),但200是强制接收的,在一序列”recv”命令中,必须至少有一个消息是强制接收的。

发送请求的时候不需要也不可能重新填写所有字段(比如说From字段不需要,因为一个dialog里的From字段都是相同的;而To字段是没办法自己填写,必需从上一个响应中引入,因为To-tag是远端加上的,本地并不知道),所以可以用[last_字段名]的方式从上一个消息中取得。通常From,To,Call-ID字段从上一个消息中取得,例如:

<send retrans="3000">
    <![CDATA[
      PRACK sip:[remote_ip]:[remote_port] SIP/2.0
      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
      CSeq: 2 PRACK
      [last_From:]
      [last_To:]
      [last_Call-ID:]
      RAck: 1 1 INVITE
      Max-Forwards: 70
      Contact: <sip:[email protected][local_ip]:[local_port];transport=udp>
      Content-Length: [len]
    ]]>
  </send>
           

在SIPp主叫脚本中,可以通过插入一个暂停语句来模拟实际持续的通话时长,例如:在建立通话后插入语句

<pause milliseconds="5000"/>

为在通话状态维持5s。

暂停语句要灵活运用,还可以在被叫脚本180Ring后插入暂停来模拟振铃时长等。

创建uas

UAS脚本以”recv”命令开始,语法规则和可用命令跟客户端UAC脚本是一样的,不过在UAS脚本中会用到很多的[last_*]关键词。

UAS脚本会首先收到UAC脚本发送的invite消息:

<recv request="INVITE">
  <action>
    <ereg regexp=".*" search_in="hdr" header="Via:" check_it="true" assign_to="4" />  
    <ereg regexp=".*" search_in="hdr" header="CSeq:" check_it="true" assign_to="2" />  
  </action>
</recv>
           

说明:在该示例中用到了正则表达式,当sipp的消息序列中带有PRACK时,UAS发送INVITE的200 OK时,某些字段(比如Via和Cseq)则不能使用[last_字段名]方式从上一个收到的消息中引入,因为此时上一个消息是PRACK,而不是INVITE,所以需要先将INVITE的这两个字段保存下来供以后使用。上面的用法便是将INVITE的Via字段的值保存为数字1,在以后发送INVITE的200 OK的时候引用,通过Action来执行这一过程。

在UAS脚本中会用到很多[last_*]关键词,例如:

<send>
    <![CDATA[
      SIP/2.0 180 Ringing
      [last_Via:]
      [last_From:]
      [last_To:];tag=[call_number]
      [last_Call-ID:]
      Require: 100rel
      CSeq: 1 INVITE
      Contact: <sip:[local_ip]:[local_port];transport=[transport]>
      Content-Length: [len]
    ]]>
  </send>
           

在这个脚本中是准备回复180消息,而且该180消息中的一些内容(Via、From、To、Call-ID)是从上一个接收的invite中提取出来的,但是Contact字段不能从上一个消息中引入。

注意:To字段从上一个消息中引入的时候,需要添加To-tag,call_number为sipp自动生成的,在一个dialog中call_number是相同的。从上一个消息引入相关字段的时候,如果上一个消息没有这个字段,则在本消息中也不会有。

在回复180Ring之后可以通过插入暂停语句来模拟真实振铃时长:

<pause milliseconds="3000"/>

注意:被叫回复200OK摘机之前,最好暂停几秒模拟振铃时长,如果180之后立刻回复200OK,容易出现早摘机异常情况。(该异常会导致主叫未收到180而直接收到200OK)

回复invite的200OK时,引用之前存储的变量:

<send >
    <![CDATA[
      SIP/2.0 200 OK
      Via:[$4]
      [last_To:]
      [last_From:]
      [last_Call-ID:]
      CSeq:[$2]
      Contact: <sip:[local_ip]:[local_port];transport=[transport]>
      Content-Type: application/sdp
      Content-Length: [len]

      v=0
      o=- 197 197 IN IP4 [local_ip]
      s=SBC call
      c=IN IP4 [media_ip]
      t=0 0
      m=audio 28190 RTP/AVP 108 106 101 102 100 111 96
      ……
    ]]>
  </send>
           

脚本中的动作action

在一个”recv”或者”recvCmd”命令中,可以执行一些动作,例如:

正则表达式(ereg)

给变量赋字符串值

记录日志(log)等等

现在主要讲一下正则表达式:

在SIPp中使用正则表达式可以实现如下功能:

提取SIP消息中的内容并存储到变量中以在后续中用到

检查SIP消息中的某些内容是否满足要求

下面是正则表达式动作的一些常用语法:

关键词 默认值 说明
regexp 用于使用正则表达式匹配接收到的消息头或者消息体。”.*”用于代表所有字符串。
search_in msg 有四个值:msg:匹配整个消息(匹配后可以再拆分)。hdr:匹配消息头(消息头匹配后不能再拆分)。body:匹配消息体。var:匹配SIPp字符串变量。
header 匹配头域,仅在search_in被设置为hdr时使用。
check_it false 设置为真时,如果不匹配则置此次呼叫为失败,不能同check_it_inverse同时使用。
assign_to 将匹配的结果存储到指定单个变量或几个变量,使用[ n]引用变量,可以将变量[ n ] 引 用 变 量 , 可 以 将 变 量 [ n]的内容应用于sip消息或者用于编写sipp条件分支脚本。

举例:提取接收到的消息Via头,将提取的Via头分配给变量1,并在后续通过[$1]引用。

<recv request="INVITE" crlf="true">
    <action>
      <ereg regexp=".*" 
            search_in="hdr" 
            header="Via:" 
            check_it="true" 
            assign_to="1" />    
    </action>
  </recv>
           

关于CSeq,RAck,以及CANCEL,ACK的特殊性

对一个会话,UAC侧和UAS侧的CSeq都是按1递增的,不过CANCEL和ACK例外,它们这两个的CSeq和INVITE一致。例如:

-->INVITE CSeq: 1 INVITE
<--100 CSeq: 1 INVITE
<--183 CSeq: 1 INVITE
-->PRACK CSeq: 2 PRACK
<--200(PRACK) CSeq: 2 PRACK
-->CANCEL CSeq: 1 CANCEL
<--200(CANCEL) CSeq: 1 CANCEL
<--487 CSeq: 1 INVITE
-->ACK CSeq: 1 ACK
           

UAC侧和UAS侧的CSeq是单独的,没有关联性,只是对于Response来说,它的CSeq需要和它对应的Request一致。

再列出一个例子:

-->INVITE CSeq: 1 INVITE
<--100 CSeq: 1 INVITE
<--183 CSeq: 1 INVITE
-->PRACK CSeq: 2 PRACK
<--200(PRACK) CSeq: 2 PRACK
-->UPDATE CSeq: 3 UPDATE
<--200(UPDATE) CSeq: 3 UPDATE
<--200(INVITE) CSeq: 1 INVITE
-->ACK CSeq: 1 ACK
-->BYE CSeq: 4 BYE
<--200(BYE) CSeq: 4 BYE
           

PRACK中有RAck这个消息头,RAck的格式是:

RAck: RSeq CSeq Method

RSeq是对应的18X中的Rseq,CSeq是对应的18X中的CSeq,也就是INVITE的CSeq,Method是INVITE。

另外要注意的是,18X重发时,每次RSeq的值是按1递增。

例子:

-->INVITE CSeq: 1 INVITE
<--100 CSeq: 1 INVITE
<--183 CSeq: 1 INVITE RSeq:100
-->PRACK CSeq: 2 PRACK  RAck:100 1 INVITE
<--200(PRACK) CSeq: 2 PRACK
<--183 CSeq: 1 INVITE RSeq:101
-->PRACK CSeq: 3 PRACK  RAck:101 1 INVITE
<--200(PRACK) CSeq: 3 PRACK
-->CANCEL CSeq: 1 CANCEL
<--200(CANCEL) CSeq: 1 CANCEL
<--487 CSeq: 1 INVITE
-->ACK CSeq: 1 ACK
           

从外部CSV文件引入变量

SIPp可以在脚本运行命令行中使用”-inf 文件名”参数来引入变量到脚本中,例如性能测试时需要模拟不同的用户同时呼叫系统,需要通过从.csv文件中引入变量的形式来实现。

文件的第一行须申明变量的读取方式是顺序读取(SEQUENTIAL),还是随机读取(RANDOM),还是基于用户的方式读取(USER)。每一行对应一个呼叫,并使用”;”分隔符分隔每一项数据,分开的项在脚本中作为变量名[filed0]、[field1]、……[fieldn]来引用。例如:

SEQUENTIAL

Sarah;sipphone32

Bob;sipphone12

Fred;sipphone94

该文件中的数据行会被按顺序读取,第一个呼叫第一行,第二个呼叫第二行。在脚本中的任何地方只要出现了关键词[field0],根据第几个呼叫决定,这个关键词就会被替换为Sarah或者Bob或者Fred,[field1]也是类似。如果达到了文件末尾则再重新开始,一直循环,文件的大小没有限制。

SIPp脚本使用示例:

sipp脚本撰写(二)

另外,可以使用参数不从第一行开始,例如从第二行开始:[field0 line=1]

还可以使用不止一个外部文件来引入变量,比如你要做一个测试主叫号码是按顺序的但是被叫是随机的时候,你就可以用一个第一行为顺序的uac.csv文件和一个第一行为随机的uas.csv文件来实现。

INVITE sip:[field0 file=”uas.csv”] SIP/2.0

From: sipp user <[field0 file=”uac.csv”]>

To: sut user <[field0 file=”uas.csv”]>

完结

继续阅读