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

被折叠的 条评论
为什么被折叠?



