石头

我的备忘录

exosip 分析

先来看看主函数main

参数分析,将分析出的参数放到cfg(struct _main_config_t)中
初始化eXosip函数库(eXosip_init)
eXosip_init
设置eXosip版本
设置全局变量eXosip里的一些参数
初始化osip函数库(osip_init)
   osip_init
   增加一个全局变量ref_count的计数(increase_ref_count),这个值用于防止某些函数(__osip_global_init)多次调用而造成一些变量多次初始化而出错. 实际上increase_ref_count这个函数有个错误的,因为它的返回值永远是0,所以如果第二次调用osip_init就会导致struct osip里的一些变量被清零(就是下一步),导致内存泄漏.不过,这也只能怪自己,谁让你多次调用osip_init呢.
   初始化struct osip的部分成员,主要是将各种状态(ICT IST NICT NIST)的事务链表清零
将全局变量eXosip的地址挂到struct osip::application_context上(osip_set_application_context),这样只要我们得到osip结构体就可以访问eXosip
设置回调函数(eXosip_set_callbacks).
   eXosip_set_callbacks
   注册发信息的回调函数(cb_snd_message)
    cb_snd_message
    得到host和port
    得到第一个via
    根据via中的协议调用cb_XXX_snd_message(XXX指udp或tcp)
     cb_udp_snd_message
     在对传入的信息做一些判断和取得目标网络的相关信息,并把message转化成字符串,最后调用_eXosip_sendto把信息发送出去
   注册一些从链表中删除事务的函数(cb_ict_kill_transaction,cb_ist_kill_transaction等)
   注册回显函数(cb_rcvresp_retransmission),这个函数并没有实际用处,只是在debug阶段显示一些信息而已,当然,你可以自己添加一些实际的功能在里面.
   注册发生错误时处理函数(cb_transport_error).这个函数的作用时在发生错误时把对应的element从eXosip的事件链表里删除掉,这样收到其他消息时就不会再去响应
   注册一些发送时显示函数(cb_sndinvite,cb_sndack等).这些函数也只是显示信息而已
   注册一些发送时显示函数(cb_rcv1xx,cb_rcv2xx等).这些函数在收到回应时调用,更改一些事件状态,向主线程的fifo添加事件,向主线程报告事件的发生(report_event)
初始化控制用的pipe(eXosip_t::j_socketctl),这个pipe用于通知子线程事件的发生
初始化事件用的pipe(eXosip_t::j_socketctl_event),用于通知主线程事件的发生
初始化fifo(osip_fifo_init).所有用于通知主线程的事件将放入这个fifo等待被响应
加载名片(jfriend_load)
加载认证信息(jidentity_load)
对参数中的协议进行映射.实际上这个又是一个错误,先映射后判断,显然错误信息永远都是要输出的
初始化监听功能(eXosip_listen_addr)
eXosip_listen_addr
一些有效性判断,得到本地ip(eXosip_guess_localip)
得到addrinfo(eXosip_get_addrinfo),并对得到的信息进行socket,bind,getsocketname,listen功能的有效性判断,直到找到满足这些条件的一个组合
对服务器进行能否连接的测试.服务器必须在50mS内有响应,否则认为不连通
创建一个线程(osip_thread_create).这个线程调用_eXosip_thread->_eXosip_execute函数.暂时先不管这个函数
如果NAT地址有定义,则设置eXosip中的防火墙地址(eXosip_masquerade_contact)
如果to已经有定义,则开始第一个call.详细流程见发起呼叫的流程
然后就是画GUI界面

先来看看发起呼叫是怎么一个流程

_josua_start_call
将各个参数(from,to,subject等)中的空格去掉(osip_clrspace),并检查from和to的有效性(_check_url)
利用传进来的参数初始化message的结构体(eXosip_call_build_initial_invite)
eXosip_call_build_initial_invite
初始化to(osip_to_init)并填充这个结构体(osip_to_parse->osip_from_parse).主要是填充 displayname/url/param(比如"kingwoo"<sip:woyaya@163.com;tag=12345>,那么 diplayname就是kingwoo,url就是sip:woyaya@163.com,param就是tag=12345)。
寻找param中的“transport”(osip_uri_uparam_get_byname).如果找到这个参数,则用这个参数和to、from 等参数一起构造一个message结构体(generating_request_out_of_dialog);否则,根据全局性的参数 (eXosip.net_interfaces[X].net_socket)决定是采用tcp还是udp,去构造这个结构体
generating_request_out_of_dialog
分析transport参数是udp还是tcp,默认是udp
初始化一个message结构体(osip_message_init),并填充方法(osip_message_set_method, method:INVITE、REGISTER等)、协议版本(osip_message_set_version)、状态机 (osip_message_set_status_code)、原因(osip_message_set_reason_phrase)
分析method是不是注册请求REGISTER。
如果是,则将route填写到Request_uri中(osip_uri_parse),from填写到to中(osip_message_set_to);
如果不是,则又有如下过程
   填充to到message结构体中(osip_message_set_to)
   如果有route,
    根据route产生一个临时结构o_proxy(osip_route_parse)
    寻找"lr"参数是否存在(osip_uri_uparam_get_byname)
    如果参数存在
     将to->url拷贝给Request_uri(osip_uri_clone)
     将上面产生的临时结构o_proxy添加到routes链表中(osip_list_add, 实际上第一个链表数据)
    如果参数不存在
     将route的url赋给Request_uri
     将to信息赋给route(osip_message_set_route)
   如果没有定义route
    将to->url拷贝给Request_uri(osip_uri_clone)
得到本地IP(eXosip_guess_ip_for_via)
填充from到message结构体中(osip_message_set_from)
填充tag到message结构体中(osip_from_set_tag)
产生一个新的CSeq(根据一个随机字符串、本地ip、方法(method)等)
设置最大跳数为70(osip_message_set_max_forwards.最大跳数指最多允许经过多少个服务器)
填充via和新生成的随机branch到message结构体中(osip_message_set_via). 这一步分服务器和客户端两种略有不同
根据各个不同的request增加各自所特需的header(主要是INVITE和SUBSCRIBE中需要增加Contact), 设置允许的响应(osip_message_set_allow)
设置“User-Agent”参数(osip_message_set_user_agent)
释放临时to这个结构体(osip_to_free)
设置“Subject”参数(osip_message_set_subject)
设置“Expires”参数,Expires=“120”(osip_message_set_expires)
设置“Supported”参数,Supported=“100rel”(osip_message_set_supported)
添加正文SDP部分(osip_message_set_body)
设置正文类型(Content-Type)为“application/sdp”(osip_message_set_content_type)
如果有多个header,则添加这些header信息(osip_message_set_multiple_header)(一般情况不用考虑)
加锁(eXosip_lock) 发送生成的message(eXosip_call_send_initial_invite)   eXosip_call_send_initial_invite   初始化一个call结构(eXosip_call_init)   事务(transaction)初始化(osip_transaction_init)    osip_transaction_init    得到当前时间(事务的生存时间起始值)    得到事务id(static int transactionid)    得到第一个via值    将via值赋给osip_transaction_t这个结构体(__osip_transaction_set_topvia)    将所有其他header也赋给这个结构体(__osip_transaction_set_XXXX)    初始化此事务专有的fifo(osip_fifo_init),osip_transaction::transactionff, 所有的此事务应该响应的事件都会被添加到这个链表里    初始化状态(osip_transaction::state, ICT_PRE_CALLING/IST_PRE_PROCEEDING/NICT_PRE_TRYING/NIST_PRE_TRYING)    初始化各种变量后根据状态(ICT,IST,NICT,NIST)设置状态并调用不同的初始化函数(主要是处理时间、路由、端口等内容,不必深究)    得到当前消息的一些信息,存于osip_event结构中(osip_new_outgoing_sipmessage)   添加用户自定义结构(osip_transaction_set_your_instance)   往上面初始化的事务专有fifo(osip_transaction::transactionff)里添加事件 (osip_transaction_add_event),即上面生成的osip_event结构.   在eXosip中增加call这个元素(ADD_ELEMENT),也就是说一个call开始了,以后可以在eXosip_t::j_calls链表中找到这个call 根据所有对话的完成情况设置一些变量(eXosip_update) 唤醒处理进程(__eXosip_wakeup)   __eXosip_wakeup   往pipe(eXosip_t::j_socketctl)里发送“w”的信息(jpipe_write)   这样在_eXosip_execute->eXosip_read_message里就可以收到这个消息.但这个消息读出后是直接被丢弃的,也就是说只是起到一个让程序从睡眠状态到工作状态转变的一个过程。实际的数据是上面osip_transaction_add_event添加的事件,这个事件在_eXosip_execute->osip_XXX_execute中被读出并处理设置reference(eXosip_call_set_reference)解锁(eXosip_unlock)


阅读更多
个人分类: sip 开源
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭