1. 初始化

session在switch_core_session.c实现,在switch_core_pvt.h定义相关数据结构。在第2章节初始化的时候,在核心初始化里会调用session初始化。

  1. void switch_core_session_init(switch_memory_pool_t *pool)
  2. {
  3. memset(&session_manager, 0, sizeof(session_manager));
  4. session_manager.session_limit = 1000;
  5. session_manager.session_id = 1;
  6. session_manager.memory_pool = pool;
  7. switch_core_hash_init(&session_manager.session_table);
  8. switch_mutex_init(&session_manager.mutex, SWITCH_MUTEX_DEFAULT, session_manager.memory_pool);
  9. switch_thread_cond_create(&session_manager.cond, session_manager.memory_pool);
  10. switch_queue_create(&session_manager.thread_queue, 100000, session_manager.memory_pool);
  11. }

...

  1. struct switch_session_manager {
  2. switch_memory_pool_t *memory_pool;
  3. switch_hash_t *session_table;
  4. uint32_t session_count;
  5. uint32_t session_limit;
  6. switch_size_t session_id;
  7. switch_queue_t *thread_queue;
  8. switch_mutex_t *mutex;
  9. switch_thread_cond_t *cond;
  10. int running;
  11. int busy;
  12. };
  13. extern struct switch_session_manager session_manager;

初始化内容比较简单,就是对session全局结构体session_manager进行初始化默认值。这个结构体管理以后创建的会话,其中哈希表session_table作为会话容器,session_count表示当前会话个数,session_limit限制最大会话个数,session_id每次创建都会++,为会话生成唯一id,thread_queue线程队列,运行sesson可以使用此线程池,当然也可以不用线程池。

  1. 创建session

当收到invite信令的时候,sip逻辑层mod_sofia就会调用switch_core_session_request_uuid创建会话。

  1. SWITCH_DECLARE(switch_core_session_t *) switch_core_session_request_uuid(switch_endpoint_interface_t
  2. *endpoint_interface,
  3. switch_call_direction_t direction,
  4. switch_originate_flag_t originate_flags,
  5. switch_memory_pool_t **pool, const char *use_uuid)
  6. {
  7. switch_memory_pool_t *usepool;
  8. switch_core_session_t *session;
  9. switch_uuid_t uuid;
  10. //判断是否已经添加到哈希表
  11. if (use_uuid && switch_core_hash_find(session_manager.session_table, use_uuid)) {
  12. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Duplicate UUID!\n");
  13. return NULL;
  14. }
  15. //申请switch_core_session_t内存
  16. session = switch_core_alloc(usepool, sizeof(*session));
  17. session->pool = usepool;
  18. switch_core_memory_pool_set_data(session->pool, "__session", session);
  19. //申请session的channel内存
  20. if (switch_channel_alloc(&session->channel, direction, session->pool) != SWITCH_STATUS_SUCCESS) {
  21. abort();
  22. }
  23. //初始化channel
  24. switch_channel_init(session->channel, session, CS_NEW, 0);
  25. if (direction == SWITCH_CALL_DIRECTION_OUTBOUND) {
  26. switch_channel_set_flag(session->channel, CF_OUTBOUND);
  27. }
  28. //设置一些session和channel的值
  29. if (use_uuid) {
  30. switch_set_string(session->uuid_str, use_uuid);
  31. else {
  32. switch_uuid_get(&uuid);
  33. switch_uuid_format(session->uuid_str, &uuid);
  34. }
  35. switch_channel_set_variable(session->channel, "uuid", session->uuid_str);
  36. switch_channel_set_variable(session->channel, "call_uuid", session->uuid_str);
  37. session->endpoint_interface = endpoint_interface;
  38. ...
  39. //创建消息队列
  40. switch_queue_create(&session->message_queue, SWITCH_MESSAGE_QUEUE_LEN, session->pool);
  41. //添加当前会话到会话容器哈希表,更新session_manager的一些值
  42. switch_mutex_lock(runtime.session_hash_mutex);
  43. switch_core_hash_insert(session_manager.session_table, session->uuid_str, session);
  44. session->id = session_manager.session_id++;
  45. session_manager.session_count++;
  46. switch_mutex_unlock(runtime.session_hash_mutex);
  47. switch_channel_set_variable_printf(session->channel, "session_id", "%u", session->id);
  48. return session;
  49. }

每个session对应一个channel,session更多地是描述信令状态,channel更多地描述传输状态。创建session的时候,同时会创建channel,session会添加到会话管理session_manager。创建的时候,会初始化session和channel的一些变量,这里比较重要的是session的消息队列message_queue。channel本章节先不讨论,下面看看session结构体。

  1. struct switch_core_session {
  2. switch_memory_pool_t *pool;
  3. switch_thread_t *thread;
  4. switch_thread_id_t thread_id;
  5. switch_endpoint_interface_t *endpoint_interface;
  6. switch_size_t id;
  7. switch_session_flag_t flags;
  8. switch_channel_t *channel;
  9. //codec相关
  10. switch_codec_t
  11. //消息队列
  12. switch_queue_t *event_queue;
  13. switch_queue_t *message_queue;
  14. //读写缓冲区相关
  15. switch_buffer_t *raw_write_buffer;
  16. switch_frame_t raw_write_frame;
  17. switch_frame_t enc_write_frame;
  18. uint8_t raw_write_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
  19. uint8_t enc_write_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
  20. switch_buffer_t *raw_read_buffer;
  21. switch_frame_t raw_read_frame;
  22. switch_frame_t enc_read_frame;
  23. uint8_t raw_read_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
  24. uint8_t enc_read_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
  25. };

session结构体有不少成员,这里列出比较重要的,一个session会有一个线程thread来处理消息,消息队列message_queue;有一个channel,有codec相关的结构体,读写一帧的缓冲区等。

  1. session线程

创建完session之后,还不能工作,会随后创建并启动session线程,至于在什么时候启动,后面分析mod_sofia会分析。这里查看session相关的API,启动session线程的接口有四个。

  1. //创建线程的四个接口
  2. switch_thread_pool_launch_thread(switch_thread_data_t **tdp);
  3. switch_core_session_thread_pool_launch(switch_core_session_t *session);
  4. switch_core_session_thread_launch(_In_ switch_core_session_t *session);
  5. switch_core_session_launch_thread(_In_ switch_core_session_t *session,
  6. _In_ void *(*func) (switch_thread_t *, void *), _In_opt_ void *obj);

前面两个是使用线程池,后面两个是普通线程。

  1. SWITCH_DECLARE(switch_status_t) switch_thread_pool_launch_thread(switch_thread_data_t **tdp)
  2. {
  3. switch_status_t status = SWITCH_STATUS_SUCCESS;
  4. switch_thread_data_t *td;
  5. td = *tdp;
  6. *tdp = NULL;
  7. status = switch_queue_push(session_manager.thread_queue, td);
  8. check_queue();
  9. return status;
  10. }
  11. SWITCH_DECLARE(switch_status_t) switch_core_session_thread_pool_launch(switch_core_session_t *session)
  12. {
  13. switch_status_t status = SWITCH_STATUS_INUSE;
  14. switch_thread_data_t *td;
  15. switch_mutex_lock(session->mutex);
  16. if (switch_test_flag(session, SSF_THREAD_RUNNING)) {
  17. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot double-launch thread!\n");
  18. else if (switch_test_flag(session, SSF_THREAD_STARTED)) {
  19. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot launch thread again after it has already been run!\n");
  20. else {
  21. switch_set_flag(session, SSF_THREAD_RUNNING);
  22. switch_set_flag(session, SSF_THREAD_STARTED);
  23. td = switch_core_session_alloc(session, sizeof(*td));
  24. td->obj = session;
  25. td->func = switch_core_session_thread;
  26. status = switch_queue_push(session_manager.thread_queue, td);
  27. check_queue();
  28. }
  29. switch_mutex_unlock(session->mutex);
  30. return status;
  31. }

如果使用线程池,则添加到会话管理session_manager.thread_queue线程队列,switch_thread_pool_launch_thread的线程执行函数由外部传入,switch_core_session_thread_pool_launch的线程执行函数是switch_core_session_thread,由此猜测,第一个函数主要给外部模块调用,第二个供core调用。然后再看看非线程池的两个接口。

  1. SWITCH_DECLARE(switch_status_t) switch_core_session_thread_launch(switch_core_session_t *session)
  2. {
  3. switch_status_t status = SWITCH_STATUS_FALSE;
  4. switch_thread_t *thread;
  5. switch_threadattr_t *thd_attr;
  6. if (switch_test_flag(session, SSF_THREAD_RUNNING) || switch_test_flag(session, SSF_THREAD_STARTED)) {
  7. status = SWITCH_STATUS_INUSE;
  8. goto end;
  9. }
  10. if (switch_test_flag((&runtime), SCF_SESSION_THREAD_POOL)) {
  11. return switch_core_session_thread_pool_launch(session);
  12. }
  13. switch_mutex_lock(session->mutex);
  14. if (switch_test_flag(session, SSF_THREAD_RUNNING)) {
  15. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot double-launch thread!\n");
  16. else if (switch_test_flag(session, SSF_THREAD_STARTED)) {
  17. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot launch thread again after it has already been run!\n");
  18. else {
  19. switch_set_flag(session, SSF_THREAD_RUNNING);
  20. switch_set_flag(session, SSF_THREAD_STARTED);
  21. switch_threadattr_create(&thd_attr, session->pool);
  22. switch_threadattr_detach_set(thd_attr, 1);
  23. switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
  24. if (switch_thread_create(&thread, thd_attr, switch_core_session_thread, session, session->pool) == SWITCH_STATUS_SUCCESS) {
  25. switch_set_flag(session, SSF_THREAD_STARTED);
  26. status = SWITCH_STATUS_SUCCESS;
  27. else {
  28. switch_clear_flag(session, SSF_THREAD_RUNNING);
  29. switch_clear_flag(session, SSF_THREAD_STARTED);
  30. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot create thread!\n");
  31. thread_launch_failure();
  32. }
  33. }
  34. switch_mutex_unlock(session->mutex);
  35. end:
  36. return status;
  37. }
  38. SWITCH_DECLARE(void) switch_core_session_launch_thread(switch_core_session_t *session, switch_thread_start_t func, void *obj)
  39. {
  40. switch_thread_t *thread;
  41. switch_threadattr_t *thd_attr = NULL;
  42. switch_threadattr_create(&thd_attr, session->pool);
  43. switch_threadattr_detach_set(thd_attr, 1);
  44. switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
  45. if (switch_thread_create(&thread, thd_attr, func, obj, session->pool) != SWITCH_STATUS_SUCCESS) {
  46. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot create thread!\n");
  47. thread_launch_failure();
  48. }
  49. }

同样的,switch_core_session_thread_launch默认使用执行函数switch_core_session_thread,switch_core_session_launch_thread使用外部自定义函数func。还有注意,把session作为参数传给switch_core_session_thread。如果全局配置标志位SCF_SESSION_THREAD_POOL,则使用线程池版本。

  1. 线程函数
  1. static void *SWITCH_THREAD_FUNC switch_core_session_thread(switch_thread_t *threadvoid *obj)
  2. {
  3. switch_core_session_t *session = obj;
  4. session->thread = thread;
  5. session->thread_id = switch_thread_self();
  6. switch_core_session_run(session);
  7. ...
  8. switch_core_session_destroy(&session);
  9. return NULL;
  10. }

session线程函数除了赋值session->thread外,只调用switch_core_session_run。

  1. 状态机state_machine
  1. SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session)
  2. {
  3. switch_channel_state_t state = CS_NEW, midstate = CS_DESTROY, endstate;
  4. const switch_endpoint_interface_t *endpoint_interface;
  5. const switch_state_handler_table_t *driver_state_handler = NULL;
  6. const switch_state_handler_table_t *application_state_handler = NULL;
  7. endpoint_interface = session->endpoint_interface;
  8. driver_state_handler = endpoint_interface->state_handler;
  9. state = switch_channel_get_state(session->channel);
  10. if (state != switch_channel_get_running_state(session->channel))
  11. {
  12. switch (state) {
  13. case CS_NEW:        /* Just created, Waiting for first instructions */
  14. switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "(%s) State NEW\n", switch_channel_get_name(session->channel));
  15. break;
  16. case CS_INIT:       /* Basic setup tasks */
  17. {
  18. STATE_MACRO(init, "INIT");
  19. }
  20. break;
  21. ...
  22. default:
  23. break;
  24. }
  25. }
  26. }

如果channel的状态改变,则根据新状态执行不同的操作,比如CS_NEW只打印一条日志,CS_INIT调用宏STATE_MACRO,这里省略了大部分case,因为基本上都是调用STATE_MACRO。

  1. STATE_MACRO
  1. #define STATE_MACRO(__STATE, __STATE_STR)                       do {
  2. //1、先进行外部回调
  3. //通道channel的状态回调
  4. while (do_extra_handlers && (application_state_handler = switch_channel_get_state_handler(session->channel, index++)) != 0) {
  5. application_state_handler->on_##__STATE(session);
  6. }
  7. //runtime的状态回调
  8. while (do_extra_handlers && proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) {
  9. }
  10. //2、驱动回调
  11. if (!driver_state_handler->on_##__STATE || (driver_state_handler->on_##__STATE(session) == SWITCH_STATUS_SUCCESS )) {
  12. }
  13. }while (silly)

STATE_MACRO宏展开大概如上,意思其实是调用状态回调接口xx_state_hander,这些回调接口有几类。第一类叫驱动回调driver_state_handler,从driver_state_handler = endpoint_interface->state_handler可以看出,驱动回调在端点接口,比如mod_sofia,在load函数中,会添加这个回调。

  1. sofia_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
  2. sofia_endpoint_interface->interface_name = "sofia";
  3. sofia_endpoint_interface->io_routines = &sofia_io_routines;
  4. sofia_endpoint_interface->state_handler = &sofia_event_handlers;
  5. sofia_endpoint_interface->recover_callback = sofia_recover_callback;
  6. switch_state_handler_table_t sofia_event_handlers = {
  7. /*.on_init */ sofia_on_init,
  8. /*.on_routing */ sofia_on_routing,
  9. /*.on_execute */ sofia_on_execute,
  10. /*.on_hangup */ sofia_on_hangup,
  11. /*.on_exchange_media */ sofia_on_exchange_media,
  12. /*.on_soft_execute */ sofia_on_soft_execute,
  13. /*.on_consume_media */ NULL,
  14. /*.on_hibernate */ sofia_on_hibernate,
  15. /*.on_reset */ sofia_on_reset,
  16. /*.on_park */ NULL,
  17. /*.on_reporting */ NULL,
  18. /*.on_destroy */ sofia_on_destroy
  19. };

应用回调application_state_handler,可以自定义往channel设置状态回调,然后此时通过switch_channel_get_state_handler就得到那些回调。

freeswitch系列4 session相关推荐

  1. freeswitch系列二 kamailio 5.0安装及实现kamailio集成freeswitch

    1. 概述 kamailio是个纯粹的SIP服务器.本文介绍先如何在debian8下安装debian5.0,然后详细介绍如何实现使用kamailio做freeswitch均衡负载.kamailio同时 ...

  2. freeswitch系列31注册流程

    freeswitch中的sip架构 freeswitch的结构图如上,蓝色箭头是调用,绿色箭头是回调.最底层的是第三方库,一个sip协议栈,实现sip信令协议,构造sip当中的各个字段.核心层主要维护 ...

  3. freeswitch系列五 解决xlite和freeswitch通话没有语音的问题

    1. 概述 本文解决如下问题:xlite之间已经正常的进行sip协议的交互,并且1008和1018已经呼通,但是1008和1018互相之间无法听到声音. 主要涉及如下内容: 1. 语音不通的原因分析 ...

  4. freeswitch系列四 通过实例学习sip协议的注册、呼叫、挂断流程

    1. 概述 本文通过tcpdump对真实环境里的软电话的注册.呼叫.挂断流程进行抓包,通过真实的例子学习SIP协议.本文主要包括以下方面: A. 详解软电话的注册时的SIP包和流程图 B. 详解软电话 ...

  5. FreeSwitch系列之内存管理

    1.sofia-sip库的内存管理 home-based 内存管理机制,在需要分配许多内存块的情况下非常有用.分配器是通过分配中心保存各个分配内存块的引用来实现的.当分配中心释放,所有它保持引用的内存 ...

  6. Web框架——Flask系列之session机制(十六)

    一.session机制图解 二.设置和获取session from flask import Flask,sessionapp = Flask(__name__)# flask的session需要用到 ...

  7. freeswitch系列21模块sofia

    mod_sofia加载 在可加载模块那一章节说过,一个模块的加载,主要是调用load函数,也可以理解为模块初始化函数,下面分析下,mod_sofia加载做了哪些事. 全局结构体mod_sofia_gl ...

  8. FreeSwitch系列之mod_sofia启动流程

    模块加载过程 启动事件处理线程池 启动事件处理线程池 : SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load)=> sofia_msg_thread_start ...

  9. webpy使用笔记(二) session的使用

    webpy使用系列之session的使用,虽然工作中使用的是django,但是自己并不喜欢那种大而全的东西~什么都给你准备好了,自己好像一个机器人一样赶着重复的基本工作,从在学校时候就养成了追究原理的 ...

最新文章

  1. mysql查询重复名字的数据都查出来_mysql查出重复的所有数据
  2. android Merger 代替 FrameLayout:布局优化
  3. IdentityServer4系列 | 客户端凭证模式
  4. FISCO BCOS 控制台 call调用已经部署的合约 不存在does not exist
  5. Linux chapter 6
  6. Sentinel 网关流量控制之Spring Cloud Gateway实战
  7. Java-多线程第四篇线程池
  8. 手撸一个基于Springboot+Vue的书籍论坛系统,可用于课程设计和毕业设计或者练手
  9. Unity 脚本生成瓦片地图TileMap
  10. ISO国家和地区代码
  11. VGA不同分辨率下的行列值(转)
  12. 我对Javascript闭包的理解
  13. 门门通还是精通一门(程序员)
  14. python程序员面试自我介绍_程序员面试要准备哪些方面的内容?
  15. 《深入理解计算机系统》实验四Architecture Lab
  16. GHGL项目-其他问题锦集
  17. 五一节日马上到来,大数据已经万事俱备,只欠东风?
  18. kettle脚本Linux执行,kettle在linux下面用于shell脚本执行:转换或者作业
  19. 计算机磁盘在线分区,电脑硬盘分区几个最好?你还把电脑硬盘分成C、D、E、F盘吗?...
  20. GeoHash介绍及使用

热门文章

  1. MCDBA 微软官方考试内容
  2. 西门子南京计算机,西门子(南京)实习面试经历
  3. 什么是替换加密(凯撒密码)?原理是什么?
  4. [C# 网络编程系列]专题五:TCP编程
  5. PTA 树的同构 思路分析及代码解析
  6. 春节之后:感悟与困惑
  7. 人工智能导论学习笔记(教材王万良《人工智能导论》(第四版)高等教育出版社 )
  8. 免费卫星图像下载网站
  9. 2023年美赛完整介绍
  10. SQL汇总统计与CUBE概念