您的位置:首页 > 编程语言

asterisk chan_sip.c代码分析(转…

2013-08-26 12:54 519 查看
 

asteriskchan_sip.c代码分析 分享

1. 代码简介:Chan_sip.c是SIP协议(RFC3261)的实现代码,它没有实现对S/MIME, TCPand TLS的支持,对应的配置文件是sip.conf,代码所在的分组是:通道驱动类(channel_drivers)。   SIP通道处理各种类型的Sipsessions和dialogs(注意:并不是所有的dialogs都是“电话呼叫”),主要包括: * - Incoming calls that will besent to the PBX core * - Outgoing calls, generated bythe PBX * - SIP subscriptions andnotifications of states and voicemail messages * - SIP registrations, bothinbound and outbound * - SIP peer management(peerpoke, OPTIONS) * - SIP text messages    在SIP通道中,通常会有一列活跃的SIP dialogs,CLI下的命令sip showchannels可以显示出大部分dialogs,除了订阅类的(它们可以用命令sip showsubscriptions显示出来)。CLI命令sip show channels的示例:Asterisk247*CLI> sip show channelsPeer            User/ANR    CallID     Seq (Tx/Rx)  Form Hold    Last Message  192.168.52.245  101        4acf6c1f558  00102/00000 ulaw No      Tx:ACK                  192.168.52.246  102        C5B3D616-26  00101/02537 ulaw No      Rx:ACK                  192.168.52.245  (None)     MDE0MzI4NTU  00101/00002 unkn No      Rx:REGISTER             3 active SIP channels 在进行代码剖析之前,先看一个重要的数据结构sip_pvt.定义在chan_sip.c中,表示一个sip dialog。sip_pvt这个结构维护了一个sip session的重要数据信息,关键字段如下:struct sip_pvt*next      指向链上的下一个sip_pvt结构。struct ast_channel* owner  指向了拥有这个结构的通道的指针struct sip_pkt*packets   维护待重传的sip packetint pendinginvite         如果有等待的邀请包,则在这里记下这个包序号struct ast_rtp*rtp       指向RTP Session的指针intrtptimeout            rtp的超时时间struct sockaddr_insa     对端地址信息 2. 代码剖析:首先chan_sip模块注册了load_module()函数作为asterisk在加载本模块时的入口函数。
17818AST_MODULE_INFOASTERISK_GPL_KEYAST_MODFLAG_DEFAULT, "Session Initiation Protocol (SIP)",
17819       .load =load_module,
17820       .unload =unload_module,
17821       .reloadreload,
17822           );
load_module()函数读取配置文件sip.conf,并且注册一个通道驱动类型,即sip,具体见sip_tech中的结构内容。
17696 if(reload_config(sip_reloadreason))
17697       returnAST_MODULE_LOAD_DECLINE
17699   
17700    if (ast_channel_registersip_tech)) {
17701      ast_logLOG_ERROR, "Unable to register channel type 'SIP'/n");
17702      io_context_destroyio);
17703      sched_context_destroysched);
17704       returnAST_MODULE_LOAD_FAILURE;
17705    }
Load_module最后调用restart_monitor()来启动sip监听。restart_monitor另外还有两处会被调用,在sip_request_call()和sip_reload()函数体内。
17735   
17736   restart_monitor();
restart_monitor调用pthread接口启动一个独立的监听线程,线程id记录在monitor_thread,线程入口函数是do_monitor()。
if (ast_pthread_create_backgrounddo_monitor, NULL) < 0) {
         ast_mutex_unlock(&monlock);
         ast_logLOG_ERROR, "Unable to start monitor thread./n");
          return -1;
       }
 do_monitor()给SIP UDPsocket添加事件处理器,sipsock_read负责读取socket收到的数据。
15233   
15234    if (sipsock> -1)
15235       sipsock_read_id =ast_io_addiosipsocksipsock_readAST_IO_IN, NULL);
do_monitor()函数然后进入一个for(;;)循环中,这个循环不断检测是否需要reloadsip模块,并且遍历sip session列表检查是否有需要kill的session。它是怎么遍历的呢?原来是chan_sip维护了一个sip_pvt结构的列表,头指针保存在全局变量iflist中,通过sip_pvt的next域进行遍历。每个sip_pvt结构记录了一个session的全部信息。变量t表示现在的时间,sip->lastrtptx表示上次发送rtp包的时间,如果两者之差大于keepalive间隔,则说明需要发送keep alive包了。
15272             if (sip->lastrtptx &&
15273                ast_rtp_get_rtpkeepalive(sip->rtp) &&
15274                 (t > sip->lastrtptx +ast_rtp_get_rtpkeepalive(sip->rtp))) {
15275               
15276                sip->lastrtptx = time(NULL);
15277               ast_rtp_sendcng(sip->rtp, 0);
15278             }
变量t表示现在的时间,sip->lastrtprx表示上次收到rtp包的时间,如果两者之差大于rpt的timeout间隔,则说明已经超时了。这两个超时参数可以在sip.conf中配置,分别如下:rtptimeout=60;rtpholdtimeout=300
if (sip->lastrtprx &&
(ast_rtp_get_rtptimeoutast_rtp_get_rtpholdtimeout(sip->rtp)) &&
(t > sip->lastrtprx +ast_rtp_get_rtptimeout(sip->rtp))) {
15282               
 此时再检测holdtimeout,并对channel上锁,ast_channel_trylock(sip->owner)。如果不是bridgedchannel,则调用soft hangup。
ast_softhangup_nolockAST_SOFTHANGUP_DEV);
 现在回过头来把焦点转移到sipsock_read()函数。所有到来的sip包都在这里开始处理,在处理sip包期间,sipsock_read需要对sip的拥有者channel上锁,sipsock_read成功则返回0,失败则返回1。它解析sip包并且找到所在的dialog,或者创建新的dialog。并且把解析好的包交给handle_request()处理。   sipsock_read第一步接收socket数据,存到结构sip_request的data域中。
res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
解析SIP包,获取sip request method,如INVITE, BYE等。
15086   parse_request(&req);
15087    req.method =find_sip_method(req.rlPart1);
随后找到对应的sip_pvt结构,或者创建新的sip_pvt结构,结构指针返回到变量p中。
15099       
15100       p =find_call(&req, &sin, req.method);
在进一步操作之前,需要对p->owner上锁,这个操作会最多尝试100次直至成功。
15107      
15108      
15109       if (!p->ownerast_channel_trylockowner))
15110          break;  
如果上锁操作失败,将会返回503 sip消息。
15127       if (req.method !=SIP_ACK)
15128         transmit_response(p, "503 Server error", &req);
15129      
15130       append_history(p, "LockFail", "Owner lock failed, transaction failed.");
15131       return 1;
更深一步的解析处理操作交给handle_request()函数处理,完了之后就是释放channel的锁。
15134    if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) {
15135      
15136       if (option_debug)
15137         ast_logLOG_DEBUG, "SIP message could not be handled, bad request: %-70.70s/n", p->callid[0] ? p->callid : "<no callid>");
15138    }
15139      
15140    if (p->owner && !nounlock)
15141      ast_channel_unlock(p->owner);
函数handle_request()视数据包的类型而处理,如果是对外出包的回应,则交给handle_response()处理,如果是一个请求包,则视请求类型(INVITE, OPTIONS, REFER, BYE,CANCELetc)交给不同的函数处理。如果是一个INVITE包,则交给handle_request_invite()处理,在那里将会创建一个新的channel,这个通道随后会执行一个单独的通道线程。这就是一个来电呼叫。如果这个呼叫被应答,则一个桥接通道或者PBX本身会回调sip_answer()函数。而真正的媒体数据,音频或者视频,则会在RTP子系统中处理,具体见rtp.c。在注册SIP通道驱动时,我们注册了一系列通道驱动的回调函数,这些有什么用呢?比如当需要发出一个outboundcall时,则会调用sip_request_call()。而当需要hangup时,则调用sip_hangup()。
01541
01542ast_channel_techsip_tech= {
01543    .type= "SIP",
01544    .description = "Session Initiation Protocol (SIP)",
01545    .capabilities = ((AST_FORMAT_MAX_AUDIO<< 1) - 1),
01546    .properties =AST_CHAN_TP_WANTSJITTERAST_CHAN_TP_CREATESJITTER,
01547    .requester =sip_request_call,
01548    .devicestate =sip_devicestate,
01549    .call =sip_call,
01550    .hangup =sip_hangup,
01551    .answer =sip_answer,
01552    .read =sip_read,
01553    .write =sip_write,
01554    .write_video =sip_write,
01555    .indicate =sip_indicate,
01556    .transfer =sip_transfer,
01557    .fixup =sip_fixup,
01558    .send_digit_begin =sip_senddigit_begin,
01559    .send_digit_end =sip_senddigit_end,
01560    .bridge =ast_rtp_bridge,
01561    .send_text =sip_sendtext,
01562    .func_channel_read =acf_channel_read,
01563 };
 现在开始分析handle_request_invite()函数。检查invite包中的headers中是否有require,最好是没有,如果有的话也必须是Replaces,其它的不支持一律不予处理。
13394   
13395    required =get_header(req, "Require");
13396    if (!ast_strlen_zero(required)) {
13397       required_profile =parse_sip_options(NULL, required);
13398       if (required_profile && required_profile !=SIP_OPT_REPLACES) {
13399         
13400         transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, required);
13401         ast_log(LOG_WARNING,"Received SIP INVITE with unsupported required extension: %s/n", required);
13402          p->invitestate =INV_COMPLETED;
13403          if (!p->lastinvite)
13404            sip_scheddestroyDEFAULT_TRANS_TIMEOUT);
13405          return -1;
13406       }
13407    }
开始验证sipuser的合法性,check_user()调用check_user_full()函数,该函数从heades中的from中取出用户名并在sipuser list 和 sip peer list中匹配,如果没找着,再查看是否允许guest,如果不允许,则认证通不过。
13584      
13585      
13586       res =check_userSIP_INVITEXMIT_RELIABLE, sin);
检查sip包中是否有SDP信息,如: application/sdp 。SDP(SessionDescriptionProtocol)是指会话描述协议,SIP包中使用它来描述语音流协议的细节,比如某端所支持的介质编码(这些编码使用RTP进行传输)。
13558         
13559          if (find_sdp(req)) {
13560             if (process_sdp(p, req)) {
13561               transmit_response(p, "488 Not acceptable here", req);
13562                if (!p->lastinvite)
13563                  sip_scheddestroyDEFAULT_TRANS_TIMEOUT);
13564                return -1;
13565             }
检查该用户的并行拨打电话数有没有达到上限。
13633      
13634       if (option_debug)
13635          ast_logLOG_DEBUG, "Checking SIP call limits for device %s/n", p->username);
13636       if ((res =update_call_counterINC_CALL_LIMIT))) {
13637          if (res < 0) {
13638            ast_logLOG_NOTICE, "Failed to place call for user %s, too many calls/n", p->username);
13639            transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req);
13640            sip_scheddestroyDEFAULT_TRANS_TIMEOUT);
13641             p->invitestateINV_COMPLETED
13642          }
13643          return 0;
13644       }
查找对应的extension,如果没有对应的extension,则从extensions开始执行(extension s是默认的extension,s表示start)。
13645       gotdest =get_destination(p, NULL);
调用sip_new()创建channel,这时候是incoming call。当调用dialapplication发起outbound call时asteriskpbx根据注册的回调函数sip_request_call()同样进入到sip_new中创建channel。
13672         
13673          c =sip_newAST_STATE_DOWNS_OR(p->username, NULL));
调用ast_pbx_start(),该函数启动一个独立线程负责这个channel,线程函数是pbx_thread(),pbx_thread()调用__ast_pbx_run()函数。
13717             res =ast_pbx_start(c);
__ast_pbx_run()函数allocate一个pbx结构和cdr结构,并把它们的指针保存到ast_channel结构的pbx域和cdr域。随后进入for循环逐个执行application。具体见./main/pbx.c。
02385      
02386       while (ast_exists_extensioncontextextenprioritycidcid_num)) {
02387          found = 1;
02388          if ((res =ast_spawn_extensioncontextextenprioritycid.cid_num))) {
02389            
下面再来分析下handle_request_bye()函数,这个函数比较简单,它在收到BYE包时被触发,首先记录 下rtp,vrtp的qos到channel内置变量,调用stop_media_flows(p)结束rtp流,调用ast_queue_hangup(p->owner)进行挂断操作,调用transmit_response(p,"200 OK", req)返回200OK消息。其中ast_queue_hangup()调用ast_queue_frame()在ast_channel机构的ast_frame队列里插入一个HANGUP的帧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: