freeswitch SIP信令的接收

本文深入探讨了Freeswitch中SIP信令的接收与处理流程,从mod_sofia加载开始,解析配置文件创建sofia_profile_t结构体,通过nua_create()设立监听端口,并在接收到SIP消息时调用sofia_event_callback()进行处理。文章详细介绍了session的创建、tech_pvt对象的初始化以及消息队列的使用,揭示了SIP请求和响应消息如何在Freeswitch内部被处理。

freeswitch SIP信令的处理在mod_sofia中
首先在模块的加载(mod_sofia_load)里面会创建一个处理SIP消息的线程:

    /* start one message thread */
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Starting initial message thread.\n");
    sofia_msg_thread_start(0);

然后会调用config_sofia函数.

    if (config_sofia(SOFIA_CONFIG_LOAD, NULL) != SWITCH_STATUS_SUCCESS) {
        mod_sofia_globals.running = 0;
        return SWITCH_STATUS_GENERR;
    }

在这个函数里面,会解析conf/sip_profiles里面的profile文件。比如我们熟悉的nternal和external文件就是在这里解析的。解析完后自然就生产了一个对应的C语言里面的sofia_profile_t结构体。
得到sofia_profile_t对象后,会调用launch_sofia_profile_thread()函数

void launch_sofia_profile_thread(sofia_profile_t *profile)
{
    //switch_thread_t *thread;
    switch_threadattr_t *thd_attr = NULL;

    switch_threadattr_create(&thd_attr, profile->pool);
    switch_threadattr_detach_set(thd_attr, 1);
    switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
    switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
    switch_thread_create(&profile->thread, thd_attr, sofia_profile_thread_run, profile, profile->pool);
}

在这里会创建一个新的线程,并在新线程里面调用nua_create()创建一个UA(User agent),在nua_create()里面就会创建对应的socket端口(比如我们熟悉的5060),在这个端口你们监听其他人发来的SIP消息。当收到SIP消息后会回调sofia_event_callback()进行处理。

profile->nua = nua_create(profile->s_root,  /* Event loop */
                                  sofia_event_callback, /* Callback for processing events */
                                  profile,  /* Additional data to pass to callback */
                                  TAG_IF( ! sofia_test_pflag(profile, PFLAG_TLS) || ! profile->tls_only, NUTAG_URL(profile->bindurl)),
                                  NTATAG_USER_VIA(1),
                                  TPTAG_PONG2PING(1),
                                  NTATAG_TCP_RPORT(0),
                                  NTATAG_TLS_RPORT(0),
                                  NUTAG_RETRY_AFTER_ENABLE(0),
                                  TAG_IF(!strchr(profile->sipip, ':'),
                                         SOATAG_AF(SOA_AF_IP4_ONLY)),
                                  TAG_IF(strchr(profile->sipip, ':'),
                                         SOATAG_AF(SOA_AF_IP6_ONLY)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
                                         NUTAG_SIPS_URL(profile->tls_bindurl)),
                                  TAG_IF(profile->ws_bindurl,
                                         NUTAG_WS_URL(profile->ws_bindurl)),
                                  TAG_IF(profile->wss_bindurl,
                                         NUTAG_WSS_URL(profile->wss_bindurl)),
                                  TAG_IF(profile->tls_cert_dir,
                                         NUTAG_CERTIFICATE_DIR(profile->tls_cert_dir)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS) && profile->tls_passphrase,
                                         TPTAG_TLS_PASSPHRASE(profile->tls_passphrase)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
                                         TPTAG_TLS_VERIFY_POLICY(profile->tls_verify_policy)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
                                         TPTAG_TLS_VERIFY_DEPTH(profile->tls_verify_depth)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
                                         TPTAG_TLS_VERIFY_DATE(profile->tls_verify_date)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS) && profile->tls_verify_in_subjects,
                                         TPTAG_TLS_VERIFY_SUBJECTS(profile->tls_verify_in_subjects)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
                                         TPTAG_TLS_CIPHERS(profile->tls_ciphers)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
                                         TPTAG_TLS_VERSION(profile->tls_version)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TLS) && profile->tls_timeout,
                                         TPTAG_TLS_TIMEOUT(profile->tls_timeout)),
                                  TAG_IF(!strchr(profile->sipip, ':'),
                                         NTATAG_UDP_MTU(65535)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_SRV),
                                         NTATAG_USE_SRV(0)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_NAPTR),
                                         NTATAG_USE_NAPTR(0)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_PINGPONG),
                                         TPTAG_PINGPONG(profile->tcp_pingpong)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_PING2PONG),
                                         TPTAG_PINGPONG(profile->tcp_ping2pong)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_SRV503),
                                         NTATAG_SRV_503(0)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_SOCKET_TCP_KEEPALIVE),
                                         TPTAG_SOCKET_KEEPALIVE(profile->socket_tcp_keepalive)),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_KEEPALIVE),
                                         TPTAG_KEEPALIVE(profile->tcp_keepalive)),
                                  NTATAG_DEFAULT_PROXY(profile->outbound_proxy),
                                  NTATAG_SERVER_RPORT(profile->server_rport_level),
                                  NTATAG_CLIENT_RPORT(profile->client_rport_level),
                                  TPTAG_LOG(sofia_test_flag(profile, TFLAG_TPORT_LOG)),
                                  TPTAG_CAPT(sofia_test_flag(profile, TFLAG_CAPTURE) ? mod_sofia_globals.capture_server : NULL),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_SIPCOMPACT),
                                         NTATAG_SIPFLAGS(MSG_DO_COMPACT)),
                                  TAG_IF(profile->timer_t1, NTATAG_SIP_T1(profile->timer_t1)),
                                  TAG_IF(profile->timer_t1x64, NTATAG_SIP_T1X64(profile->timer_t1x64)),
                                  TAG_IF(profile->timer_t2, NTATAG_SIP_T2(profile->timer_t2)),
                                  TAG_IF(profile->timer_t4, NTATAG_SIP_T4(profile->timer_t4)),
                                  SIPTAG_ACCEPT_STR("application/sdp, multipart/mixed"),
                                  TAG_IF(sofia_test_pflag(profile, PFLAG_NO_CONNECTION_REUSE),
                                         TPTAG_REUSE(0)),
                                  TAG_END());   /* Last tag should always finish the sequence */

创建UA后还会创建一个Endpoint,并给这个Endpoint绑定state_handler,io_routines等回调函数。state_handler是状态处理函数,当Endpoint里面的session的状态发生变化时会回调这些函数。io_routines是一系列常用的io操作,比如发起外呼,读取音频数据帧等。

    /* connect my internal structure to the blank pointer passed to me */
    *module_interface = switch_loadable_module_create_module_interface(pool, modname);
    sofia_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
    sofia_endpoint_interface->interface_name = "sofia";
    sofia_endpoint_interface->io_routines = &sofia_io_routines;
    sofia_endpoint_interface->state_handler = &sofia_event_handlers;
    sofia_endpoint_interface->recover_callback = sofia_recover_callback;

如上面说的在收到一个SIP消息后会调用sofia_event_callback()处理对应的消息。在这里我们可以看到各种熟悉的信令了。我们一INVITE为例分析一下。在这个之前先提一下一个重要的函数:

void nua_respond(nua_handle_t *nh,
         int status, char const *phrase,
         tag_type_t tag, tag_value_t value,
         ...)

这个函数就是发送SIP信令。
然后继续讨论一下sofia_event_callback()函数,如果事件的类型为:nua_i_invite,会先调用switch_core_session_request_uuid()创建一个session,如果session创建成功,还会创建一个对应的tech_pvt对象。

        if (sofia_test_pflag(profile, PFLAG_CALLID_AS_UUID)) {
            session = switch_core_session_request_uuid(sofia_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL, sip->sip_call_id->i_id);
        } else {
            session = switch_core_session_request(sofia_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL);
        }

        if (session) {
            const char *channel_name = NULL;
            tech_pvt = sofia_glue_new_pvt(session);

初始化并设置tech_pvt结构体。然后调用sofia_glue_attach_private把这个对象绑定到对应的session上面。

            sofia_glue_attach_private(session, profile, tech_pvt, channel_name);

            set_call_id(tech_pvt, sip);

最后调用sofia_queue_message(de);将事件放到一个队里里面去,以处理一些耗时的操作。

//static int foo = 0;
void sofia_queue_message(sofia_dispatch_event_t *de)
{
    int launch = 0;

    if (mod_sofia_globals.running == 0 || !mod_sofia_globals.msg_queue) {
        sofia_process_dispatch_event(&de);
        return;
    }


    if (de->profile && sofia_test_pflag(de->profile, PFLAG_THREAD_PER_REG) &&
        de->data->e_event == nua_i_register && DE_THREAD_CNT < mod_sofia_globals.max_reg_threads) {
        sofia_process_dispatch_event_in_thread(&de);
        return;
    }


    if ((switch_queue_size(mod_sofia_globals.msg_queue) > (SOFIA_MSG_QUEUE_SIZE * (unsigned int)msg_queue_threads))) {
        launch++;
    }


    if (launch) {
        if (mod_sofia_globals.msg_queue_len < mod_sofia_globals.max_msg_queues) {
            sofia_msg_thread_start(mod_sofia_globals.msg_queue_len + 1);
        }
    }

    switch_queue_push(mod_sofia_globals.msg_queue, de);
}

在这里面先判断一下是否需要增加消费线程,要的话就增加,然后调用switch_queue_push()将消息推送到真正的消息队里里面。
刚刚启动的那些消费线程,真正的执行函数是:sofia_msg_thread_run()

//static int count = 0;

void *SWITCH_THREAD_FUNC sofia_msg_thread_run(switch_thread_t *thread, void *obj)
{
    void *pop;
    switch_queue_t *q = (switch_queue_t *) obj;
    int my_id;


    for (my_id = 0; my_id < mod_sofia_globals.msg_queue_len; my_id++) {
        if (mod_sofia_globals.msg_queue_thread[my_id] == thread) {
            break;
        }
    }

    switch_mutex_lock(mod_sofia_globals.mutex);
    msg_queue_threads++;
    switch_mutex_unlock(mod_sofia_globals.mutex);

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MSG Thread %d Started\n", my_id);


    for(;;) {

        if (switch_queue_pop(q, &pop) != SWITCH_STATUS_SUCCESS) {
            switch_cond_next();
            continue;
        }

        if (pop) {
            sofia_dispatch_event_t *de = (sofia_dispatch_event_t *) pop;
            sofia_process_dispatch_event(&de);
        } else {
            break;
        }
    }

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MSG Thread Ended\n");

    switch_mutex_lock(mod_sofia_globals.mutex);
    msg_queue_threads--;
    switch_mutex_unlock(mod_sofia_globals.mutex);

    return NULL;
}

所以实际调用的是sofia_process_dispatch_event(),而sofia_process_dispatch_event()实际调用的是:our_sofia_event_callback()来处理接收到的消息。在这里我们终于看到各种事件的处理了。在switch的各个分支中,我们可以看到许多以nua_r和nua_i开头的SIP event。其中,前者标识收到一条响应(Response)消息,后者表示收到一条请求消息。当收到一条请求消息时:

    case nua_i_invite:
        if (session && sofia_private) {
            if (sofia_private->is_call > 1) {
                sofia_handle_sip_i_reinvite(session, nua, profile, nh, sofia_private, sip, de, tags);
            } else {
                sofia_private->is_call++;
                sofia_handle_sip_i_invite(session, nua, profile, nh, sofia_private, sip, de, tags);
            }
        }
        break;

也就是会调用sofia_handle_sip_i_invite来处理。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值