zoukankan      html  css  js  c++  java
  • asterisk chan_sip.c代码分析

    1. 代码简介:

    Chan_sip.c是SIP协议(RFC3261)的实现代码,它没有实现对S/MIME, TCP and TLS的支持,对应的配置文件是sip.conf,代码所在的分组是:通道驱动类(channel_drivers)。

        SIP通道处理各种类型的Sip sessions和dialogs(注意:并不是所有的dialogs都是“电话呼叫”),主要包括:

     * - Incoming calls that will be sent to the PBX core

     * - Outgoing calls, generated by the PBX

     * - SIP subscriptions and notifications of states and voicemail messages

     * - SIP registrations, both inbound and outbound

     * - SIP peer management (peerpoke, OPTIONS)

     * - SIP text messages

     

        在SIP通道中,通常会有一列活跃的SIP dialogs,CLI下的命令sip show channels可以显示出大部分dialogs,除了订阅类的(它们可以用命令sip show subscriptions显示出来)。

    CLI命令sip show channels的示例:

    Asterisk*CLI> sip show channels

    Peer             User/ANR    Call ID      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 packet

    int pendinginvite          如果有等待的邀请包,则在这里记下这个包序号

    struct ast_rtp* rtp        指向RTP Session的指针

    int rtptimeout             rtp的超时时间

    struct sockaddr_in sa      对端地址信息

    2. 代码剖析:

    首先chan_sip模块注册了load_module()函数作为asterisk在加载本模块时的入口函数。

    17818
    AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Session Initiation Protocol (SIP)", 17819       .load = load_module, 17820       .unload = unload_module, 17821       .reload = reload, 17822 );

    load_module()函数读取配置文件sip.conf,并且注册一个通道驱动类型,即sip,具体见sip_tech中的结构内容。

    17696 if(reload_config(sip_reloadreason)) /* Load the configuration from sip.conf */
    17697       return AST_MODULE_LOAD_DECLINE;
    17699    /* Make sure we can register our sip channel type *
    17700    if (ast_channel_register
    (&sip_tech)) {
    17701       ast_log(LOG_ERROR, "Unable to register channel type 'SIP'/n");
    17702       io_context_destroy(io);
    17703       sched_context_destroy(sched);
    17704       return AST_MODULE_LOAD_FAILURE;
    17705    }

    Load_module最后调用restart_monitor()来启动sip监听。restart_monitor另外还有两处会被调用,在sip_request_call()和sip_reload()函数体内。

    17735    /* And start the monitor for the first time */
    17736    restart_monitor();

    restart_monitor调用pthread接口启动一个独立的监听线程,线程id记录在monitor_thread,线程入口函数是do_monitor()。

    if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
          ast_mutex_unlock(&monlock);
          ast_log(LOG_ERROR, "Unable to start monitor thread./n");
              return -1;
    }

     

    do_monitor()给SIP UDP socket添加事件处理器,sipsock_read负责读取socket收到的数据。

    15233    /* Add an I/O event to our SIP UDP socket */
    15234    if (sipsock> -1)
    15235       sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);

    do_monitor()函数然后进入一个for(;;)循环中,这个循环不断检测是否需要reload sip模块,并且遍历sip session列表检查是否有需要kill的session。它是怎么遍历的呢?原来是chan_sip 维护了一个sip_pvt结构的列表,头指针保存在全局变量iflist中,通过sip_pvt的next域进行遍历。每个sip_pvt结构记录了一个 session的全部信息。

    变量t表示现在的时间,sip->lastrtptx表示上次发送rtp包的时间,如果两者之差大于keep alive间隔,则说明需要发送keep alive包了。

    15272             if (sip->lastrtptx &&
    15273                 ast_rtp_get_rtpkeepalive(sip->rtp) &&
    15274                 (t > sip->lastrtptx + ast_rtp_get_rtpkeepalive(sip->rtp))) {
    15275                /* Need to send an empty RTP packet */
    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_rtptimeout(sip->rtp) || ast_rtp_get_rtpholdtimeout(sip->rtp)) && (t > sip->lastrtprx + ast_rtp_get_rtptimeout(sip->rtp))) {
    15282                /* Might be a timeout now -- see if we're on hold */

     

    此时再检测holdtimeout,并对channel上锁,ast_channel_trylock(sip->owner)。如果不是bridged channel,则调用soft hangup。

    /* Issue a softhangup */
    ast_softhangup_nolock(sip->owner, AST_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       /* Find the active SIP dialog or create a new one */
    15100       p = find_call(&req, &sin, req.method); /* returns p locked */

    在进一步操作之前,需要对p->owner上锁,这个操作会最多尝试100次直至成功。

    15107       /* Go ahead and lock the owner if it has one -- we may need it */
    15108       /* becaues this is deadlock-prone, we need to try and unlock if failed */
    15109       if (!p->owner || !ast_channel_trylock(p->owner))
    15110          break;   /* locking succeeded */

    如果上锁操作失败,将会返回503 sip消息。

    15127       if (req.method != SIP_ACK)
    15128          transmit_response(p, "503 Server error", &req); /* We must respond according to RFC 3261 sec 12.2 */
    15129       /* XXX We could add retry-after to make sure they come back */
    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       /* Request failed */
    15136       if (option_debug
    )
    15137          ast_log(LOG_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, CANCEL etc)交给不同的函数处理。如果是一个INVITE包,则交给handle_request_invite()处理,在那里将会创建一个新的 channel,这个通道随后会执行一个单独的通道线程。这就是一个来电呼叫。如果这个呼叫被应答,则一个桥接通道或者PBX本身会回调 sip_answer()函数。而真正的媒体数据,音频或者视频,则会在RTP子系统中处理,具体见rtp.c。

    在注册SIP通道驱动时,我们注册了一系列通道驱动的回调函数,这些有什么用呢?比如当需要发出一个outbound call时,则会调用sip_request_call()。而当需要hangup时,则调用sip_hangup()。

    01541 /*! /brief Definition of this channel for PBX channel registration */
    01542
     static const struct ast_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_WANTSJITTER| AST_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    /* Find out what they require */
    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          /* At this point we only support REPLACES */
    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_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    13405          return -1;
    13406       }
    13407    }

    开始验证sip user的合法性,check_user()调用check_user_full()函数,该函数从heades中的from中取出用户名并在sip user list 和 sip peer list中匹配,如果没找着,再查看是否允许guest,如果不允许,则认证通不过。

    13584       /* This is a new invite */
    13585       /* Handle authentication if this is our first invite */
    13586       res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);

    检查sip包中是否有SDP信息,如: application/sdp 。SDP(Session Description Protocol)是指会话描述协议,SIP包中使用它来描述语音流协议的细节,比如某端所支持的介质编码(这些编码使用RTP进行传输)。

    13558          /* Handle SDP here if we already have an owner */
    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_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    13564                return -1;
    13565             }

    检查该用户的并行拨打电话数有没有达到上限。

    13633       /* Check number of concurrent calls -vs- incoming limit HERE */
    13634       if (option_debug)
    13635          ast_log(LOG_DEBUG, "Checking SIP call limits for device %s/n", p->username);
    13636       if ((res = update_call_counter(p, INC_CALL_LIMIT))) {
    13637          if (res < 0) {
    13638             ast_log(LOG_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_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    13641             p->invitestate = INV_COMPLETED
    13642          }
    13643          return 0;
    13644       }

    查找对应的extension,如果没有对应的extension,则从extension s开始执行(extension s是默认的extension,s表示start)。

    13645       gotdest = get_destination
    (p, NULL); /* Get destination right away */

    调用sip_new()创建channel,这时候是incoming call。当调用dial application发起outbound call时asterisk pbx根据注册的回调函数sip_request_call()同样进入到sip_new中创建channel。

    13672          /* First invitation - create the channel */
    13673          c = sip_new(p, AST_STATE_DOWN, S_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       /* loop on priorities in this context/exten */
    02386       while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
    02387          found = 1;
    02388          if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
    02389             /* Something bad happened, or a hangup has been requested. */

    下面再来分析下handle_request_bye()函数,这个函数比较简单,它在收到BYE包时被触发,首先记录 下rtp, vrtp的qos到channel内置变量,调用stop_media_flows(p)结束rtp流,调用 ast_queue_hangup(p->owner)进行挂断操作,调用transmit_response(p, "200 OK", req)返回200 OK消息。其中ast_queue_hangup()调用ast_queue_frame()在ast_channel机构的ast_frame队列里插 入一个HANGUP的帧。

  • 相关阅读:
    python自定义ORM并操作数据库
    python 元类理解
    Python中type()详解:动态创建类
    Centos7安装教程
    数据库建模:学生信息系统搭建实验
    数据库索引、视图与触发器
    用python批量插入数据到数据库中
    制作一个简单的部门员工知识分享的python抽取脚本
    【Mybatis】XML配置实现增删改查
    NIO,AIO,BIO
  • 原文地址:https://www.cnblogs.com/xiaOt119/p/2538741.html
Copyright © 2011-2022 走看看