深入浅出asterisk(三):chan_sip代码分析(下)

原创 2007年09月28日 16:54:00

现在回过头来把焦点转移到sipsock_read()函数。所有到来的sip包都在这里开始处理,在处理sip包期间,sipsock_read需要对sip的拥有者channel上锁,sipsock_read成功则返回0,失败则返回1。它解析sip包并且找到所在的dialog,或者创建新的dialog。并且把解析好的包交给handle_request()处理。

    sipsock_read第一步接收socket数据,存到结构sip_requestdata域中。

15062    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

Outbound calls

 Outbound calls are set up by the PBX through the sip_request_call() function. After that, they are activated by sip_call().

Hanging up

 The PBX issues a hangup on both incoming and outgoing calls through the sip_hangup() function

 

01502 /*------Request handling functions */
01503 static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock);
01504 static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock);
01505 static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, int *nounlock);
01506 static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
01507 static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e);
01508 static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
01509 static int handle_request_message(struct sip_pvt *p, struct sip_request *req);
01510 static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e);
01511 static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
01512 static int handle_request_options(struct sip_pvt *p, struct sip_request *req);
01513 static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, struct sockaddr_in *sin);
01514 static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e);
01515 static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno);
01516 
01517 /*------Response handling functions */
01518 static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
01519 static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
01520 static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno);
01521 static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno);

    现在回顾一下注册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_tech sip_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()函数。If the INVITE has a Replaces header, it is part of an attended transfer. If so, we do not go through the dial plan but tries to find the active call and masquerade into it。(不是很明白?)

检查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    }

接下来检查headers中是否有Replaces,如果有则做相关处理。由于商业项目中很少涉及这种需求,则略过。

开始验证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是默认的extensions表示start

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

调用sip_new()创建channel,这时候是incoming call。当调用dial application发起outbound callasterisk 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 vrtpqoschannel内置变量,调用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的帧。

 
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Asterisk + Radius 简易攻略

Asterisk号称是VOIP领域的瑞士军刀,Radius是AAA计费协议的圆月弯刀。且看Asterisk+Radius如何双刀合璧,称雄VOIP的江湖。。。     Asterisk 是一个开放源代...

深入浅出asterisk(一):asterisk通道(Channels)

由于工作上的VOIP项目需要,最近亲密接触了一下asterisk工程代码。关于asterisk在VOIP上的伟大地位和作用,俺就不絮叨了,请看前作:Astersik+Radius简易攻略    国内对...

深入浅出asterisk(四):部署CDR MySql模块

Asterisk可以存储CDR记录到一个MYSQL数据库中,也可以选择以CSV文本文件的形式保存起来。由于Mysql客户端开发库的版权问题,Mysql billing应用程序不再作为Asterisk标...

深入浅出asterisk(二):chan_sip代码分析(上)

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

Linux C语言 网络编程(二) 服务器模型

前面介绍了关于连接linux服务端方式,但是服务端的资源是有限的,所以我们通常需要重新思考,设计一套服务器模型来处理对应的客户端的请求。第一种:并发服务器,通过主进程统一处理客户端的连接,当客户端连接...

使用友善之臂6410开发板NFS启动时打印乱码

使用友善之笔的6410开发板进行NFS启动的时候,发现在解压完内核开始运行后,串口就打印乱码,刚开始以为是NFS配置有问题,发现也都可以正常用,后来尝试改将串口改为USB转串口的配置,因为我的串口是U...

你也可以玩转Skype -- 基于Skype API开发外壳程序入门

Skype是目前这个星球上最厉害的IM+VOIP软件,Skype现在已经改变了全球2.8亿人的生活方式。你,值得拥有! :)Skype中文官网:http://skype.tom.com/Skype全球...

【树莓派开发】树莓派3B的Qt交叉编译环境搭建(1)

实验室项目需要用Qt在树莓派上开发一个用户交互界面。虽然树莓派的硬件配置随版本更新不断提升,但是还是无法满足直接在树莓派上进行Qt开发,所以首先要做的事情就是搭建交叉编译环境。 下面开始搭建步骤: 实...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)