FreeSWITCH - mod_xml_rpc源码分析七server.c

来源:互联网 发布:手机存储卡数据恢复 编辑:程序博客网 时间:2024/05/16 03:00

下面分析过程中函数先后次序,采用mod_xml_rpc.c文件内SWITCH_MODULE_RUNTIME_FUNCTION(mod_xml_rpc_runtime)函数里abyss库函数的出现顺序。

ServerCreate

abyss_boolServerCreate(TServer *       const serverP,             const char *    const name,             xmlrpc_uint16_t const portNumber,             const char *    const filesPath,             const char *    const logFileName)
这应该是使用abyss库的客户代码首要调用的函数。函数内代码很容易理解,首先调用createServer进行初始化操作,如果初始化成功则再调用setNamePathLog函数初始化主目录以及日志文件等信息。客户代码内的TServer定义其实没什么实质内容,内部只是一个指向_TServer的指针。这个结构体内部的信息都只由库代码访问。

typedef struct {    /* Before Xmlrpc-c 1.04, the internal server representation,       struct _TServer, was exposed to users and was the only way to       set certain parameters of the server.  Now, use the (new)       ServerSet...() functions.  Use the HAVE_ macros to determine       which method you have to use.    */    struct _TServer * srvP;} TServer;

createServer

在这个函数内,果然看到了为_TServer变量申请内存初始化变量的代码。创建完变量后,并没有做其他更多的处理,只是为三个函数指针赋上了相应的函数。HandlerCreate和HandlerDefaultBuiltin函数均在handler.c函数内定义。

setNamePathLog

ServerCreate函数内另一个被调用的函数是setNamePathLog。这个函数内一次调用了另三个函数:ServerSetName、ServerSetFilesPath和ServerSetLogFileName。第一个函数只是为_TServer变量的内部name属性赋值。ServerSetLogFileName函数的作用也只是给_TServer变量的logfilename赋值。ServerSetFilesPath函数是对另一个函数的封装:HandlerSetFilesPath。并且向HandlerSetFilesPath函数传递了_TServer的builtinHandlerP函数指针。这个函数指针值在createServer函数内被赋值,指向的是HandlerCreate。HandlerSetFilesPath也是在handler.c文件中定义。突然发现刚才分析代码时出错了,createServer函数内并不是将HandlerCreate函数赋给_TServer结构体内的属性,而是调用HandlerCreate函数创建一个BIHandler变量。这个被创建的BIHandler变量将赋给_TServer结构体内的builtinHandlerP属性。BIHandler结构体见下图:

struct BIHandler {    const char * filesPath;    TList defaultFileNames;    MIMEType * mimeTypeP;        /* NULL means to use the global MIMEType object */};
HandlerCreate函数内会初始化这个结构体内部的TList。这个结构体在分析data.c代码时曾经研究过。HandlerSetFilesPath这个函数其实也不复杂,只是将主目录赋给BIHandler结构体的filesPath属性。


ServerInit

客户代码调用完ServerCreate函数后,就应该接着调用ServerInit函数了。第一步就是要创建一个TChanSwitch变量,并赋给_TServer的chanSwitchP属性。为了达到这个目的,最终调用的是ChanSwitchWinCreate函数,此函数在socket_win.c文件内,曾经研究过。这个函数不但创建了TChanSwitch变量,而且还生成了一个socket,并且与特定的port端口绑定成功。第一步完成后,接着就是调用ChanSwitchListen函数。

voidChanSwitchListen(TChanSwitch * const chanSwitchP,                 uint32_t      const backlog,                 const char ** const errorP) {    if (SwitchTraceIsActive)        fprintf(stderr, "Channel switch %p listening.\n", chanSwitchP);    (*chanSwitchP->vtbl.listen)(chanSwitchP, backlog, errorP);}
代码显示最终访问了vtbl下的listen函数。这个listen实际指向的是socket_win.c代码内的chanSwitchListen静态函数。

ServerAddHandler

这个函数最主要的输入参数是处理函数。首先,依据这个传入的函数创建一个URIHandler2的结构体变量,并将handleReq1属性指向这个函数。然后将这个URIHandler2变量放入_TServer的handlers这个TList链表结构体内。

static URIHandler2 *createHandler(URIHandler const function) {    URIHandler2 * handlerP;    MALLOCVAR(handlerP);    if (handlerP != NULL) {        handlerP->init       = NULL;        handlerP->term       = NULL;        handlerP->userdata   = NULL;        handlerP->handleReq2 = NULL;        handlerP->handleReq1 = function;    }    return handlerP;}


ServerRun

static void serverRun2(TServer * const serverP) {    struct _TServer * const srvP = serverP->srvP;    outstandingConnList * outstandingConnListP;    createOutstandingConnList(&outstandingConnListP);    while (!srvP->terminationRequested)        acceptAndProcessNextConnection(serverP, outstandingConnListP);    waitForNoConnections(outstandingConnListP);        destroyOutstandingConnList(outstandingConnListP);}void ServerRun(TServer * const serverP) {    struct _TServer * const srvP = serverP->srvP;    if (!srvP->chanSwitchP)        TraceMsg("This server is not set up to accept connections "                 "on its own, so you can't use ServerRun().  "                 "Try ServerRunConn() or ServerInit()");    else        serverRun2(serverP);}
ServerRun函数简单的封装了下serverRun2函数,执行流程最终会调用serverRun2。在实际接收请求连接的客户端前,将创建outstandingConnList类型的变量。outstandingConnList这个类型,以及createOutstandingConnList函数都不复杂。但这里出现的TConn之前确实没看到过,感觉与TChannel应该有些概念上相似。conn.c文件内有TConn的定义。
typedef struct {    TConn * firstP;    unsigned int count;        /* Redundant with 'firstP', for quick access */} outstandingConnList;static voidcreateOutstandingConnList(outstandingConnList ** const listPP) {    outstandingConnList * listP;    MALLOCVAR_NOFAIL(listP);    listP->firstP = NULL;  /* empty list */    listP->count = 0;    *listPP = listP;}

outstandingConnList类型的变量创建完毕后,将它与TServer变量一并交给acceptAndProcessNextConnection函数。从字面上来理解这个函数,它的作用应该就是接收一个新的连接。while循环的作用就是当TServer没有被设置成关闭始终都可以接收来自客户端的连接请求。函数余下的部分就是TServer关闭后的清理工作。waitForNoConnections的作用就是清理掉所有已建立好的连接(由于牵扯到TConn类型,所以内部实现还不甚明了)。destroyOutstandingConnList函数只是释放outstandingConnList类型的变量占用的内存空间。ServerRun函数有一点要注意,因为它有一个几乎是无效循环过程,所以调用此函数的代码必须处于一个单独的线程内。接下来继续分析acceptAndProcessNextConnection函数。首先调用了ChanSwitchAccept函数,这个函数在分析chanswitch.c文件时提到过,它最终调用的是socket_win.c文件内的chanSwitchAccept函数。如果一切正常,将创建好TChannel变量以及abyss_win_chaninfo结构体变量。然后再利用ConnCreate创建TConn类型的变量。TConn变量创建好后,会将它放入outstandingConnListP链表内,然后再调用ConnProcess启动针对此连接的读写处理,我想这应该是启动一个线程。

static voidacceptAndProcessNextConnection(    TServer *             const serverP,    outstandingConnList * const outstandingConnListP) {    struct _TServer * const srvP = serverP->srvP;    TConn * connectionP;    const char * error;    TChannel * channelP;    void * channelInfoP;            ChanSwitchAccept(srvP->chanSwitchP, &channelP, &channelInfoP, &error);        if (error) {        TraceMsg("Failed to accept the next connection from a client "                 "at the channel level.  %s", error);        xmlrpc_strfree(error);    } else {        if (channelP) {            const char * error;            freeFinishedConns(outstandingConnListP);                        waitForConnectionCapacity(outstandingConnListP);                        ConnCreate(&connectionP, serverP, channelP, channelInfoP,                       &serverFunc, &destroyChannel, ABYSS_BACKGROUND,                       srvP->useSigchld,                       &error);            if (!error) {                addToOutstandingConnList(outstandingConnListP,                                         connectionP);                ConnProcess(connectionP);                /* When connection is done (which could be later, courtesy                   of a background thread), destroyChannel() will                   destroy *channelP.                */            } else {                xmlrpc_strfree(error);                ChannelDestroy(channelP);                free(channelInfoP);            }        } else {            /* Accept function was interrupted before it got a connection */        }    }}
创建TConn之前还调用了另两个函数:freeFinishedConns和waitForConnectionCapacity。前一个函数的作用是清理已经处于关闭状态的的连接,后一个函数的作用是不让连接数超过预设值。


使用规则

经过上述几步分析,可以获知客户代码大致的使用规则应该是:

1、调用ServerCreate初始化;

2、调用ServerInit建立内部的socket,开始侦听;

3、调用ServerAddHandller注册客户代码特定的处理函数;

4、调用ServerRun启动内部处理框架。


但这些代码感觉还不足以构成一个完整的处理过程。不知道新的客户端连接上后读写数据是如何运作的,连接断开是如何侦测到的,等等。上面的分析有两个函数没有仔细研究过:ConnCreate和ConnProcess,它们属于conn.c文件内。如果想知道上述提到的这些内容,可以直接转至下一篇文档。


serverFunc

如果转去看过了conn.c内的ConnCreate和ConnProcess两个函数分析,可以知道本文件内的serverFunc是与客户端连接相关的线程主函数。

static voidserverFunc(void * const userHandle) {/*----------------------------------------------------------------------------   Do server stuff on one connection.  At its simplest, this means do   one HTTP request.  But with keepalive, it can be many requests.-----------------------------------------------------------------------------*/    TConn *           const connectionP = userHandle;    struct _TServer * const srvP = connectionP->server->srvP;    unsigned int requestCount;        /* Number of requests we've handled so far on this connection */    bool connectionDone;        /* No more need for this HTTP connection */    requestCount = 0;    connectionDone = FALSE;    while (!connectionDone) {        bool success;                /* Wait to read until timeout */        success = ConnRead(connectionP, srvP->keepalivetimeout);        if (!success)            connectionDone = TRUE;        else {            bool const lastReqOnConn =                requestCount + 1 >= srvP->keepalivemaxconn;            bool keepalive;                        processDataFromClient(connectionP, lastReqOnConn, srvP->timeout,                                  &keepalive);                        ++requestCount;            if (!keepalive)                connectionDone = TRUE;                        /**************** Must adjust the read buffer *****************/            ConnReadInit(connectionP);        }    }}
这个函数最主要的就是一个循环过程。结束这个循环的条件是connectionDone变量值是FALSE。即,如果处理过程未显示连接已结束那么循环将一直持续下去。循环分三部分:读取数据、处理数据和调整内部接收数据缓存区。读取数据由ConnRead函数完成。处理数据由processDataFromClient函数完成。调整缓存区由ConnReadInit函数完成。在下一篇文章中会仔细分析ConnRead函数。ConnRead函数执行完毕后,获取到的数据被放置在TConn的buffer数组内。

在调用processDataFromClient函数前,看到有这么一条语句。

            bool const lastReqOnConn =                requestCount + 1 >= srvP->keepalivemaxconn;

_TServer的keepalivemaxconn属性在_TServer被创建时赋值为30。这个属性从字面上理解应该就是最多保持30个连接。但又发现这是一个serverFunc函数内的变量,不是全局变量,且是在每次收到数据和处理后累加一。因此判断它的含义应该是单个客户端连接最多只能处理30个请求。难道超过了这个请求数后就不再处理了,删除这个连接?serverFunc函数内代码还无法完全解释上述疑问。


processDataFromClient

static voidprocessDataFromClient(TConn *  const connectionP,                      bool     const lastReqOnConn,                      uint32_t const timeout,                      bool *   const keepAliveP) {    TSession session = {0};  /* initilization, an afforadble alternative to random memory being misinterpreted! */    RequestInit(&session, connectionP);    session.serverDeniesKeepalive = lastReqOnConn;            RequestRead(&session, timeout);    if (session.status == 0) {        if (session.version.major >= 2)            ResponseStatus(&session, 505);        else if (!RequestValidURI(&session))            ResponseStatus(&session, 400);        else            runUserHandler(&session, connectionP->server->srvP);    }    assert(session.status != 0);    if (session.responseStarted)        HTTPWriteEndChunk(&session);    else        ResponseError(&session);    *keepAliveP = HTTPKeepalive(&session);    SessionLog(&session);    RequestFree(&session);}
这个函数内出现的很多函数都存在于http.c文件内。RequestInit函数的作用只是初始化TSession变量。接着用lastReqOnConn为TSession的serverDeniesKeepalive赋值。这个值在分析serverFunc时曾提到过。接着就是调用RequestRead函数。此函数的前部有一段说明文字可以帮助理解函数的作用。
/*----------------------------------------------------------------------------   Read the headers of a new HTTP request (assuming nothing has yet been   read on the session).<span style="font-family: Arial, Helvetica, sans-serif;">读取一个新的HTTP请求的headers(假定有关这个session的任何读操作还未执行)。</span>   Update *sessionP with the information from the headers.<span style="font-family: Arial, Helvetica, sans-serif;">用headers里的信息更新sessionP变量。</span>   Leave the connection positioned to the body of the request, ready   to be read by an HTTP request handler (via SessionRefillBuffer() and   SessionGetReadData()).<span style="font-family: Arial, Helvetica, sans-serif;">让connection定位(应该指的是connection的内部缓存区的定位)到请求的boby部分(应该指的是HTTP请求的body区域)。后续使用HTTP请求处理器(SessionRefillBuffer和SessionGetReadData函数)将从这里开始处理。</span>-----------------------------------------------------------------------------*/
这一段文字简明扼要的表明了这个函数的处理。RequestRead函数的第二个输入参数是个超时值,这个数值在_TServer创建时就设定好了,是15。现在还不知道在RequestRead函数内这个数值的用途是什么。RequestRead函数内首先调用readRequestHeader函数找到请求行的位置。然后再使用parseRequestLine函数解析请求行,得到版本号、主机以及端口等信息。接着用这些解析出的数据填充TSession的requestInfo属性(initRequestInfo)。如果在解析RequestLine时发现后面还有其他header,那么在initRequestInfo后继续使用readAndProcessHeaders函数解析出其余的headers。至此,RequestRead函数执行完毕,执行又回到processDataFromClient函数内。

在检测完版本以及URI的有效性验证这两步后,调用runUserHandler函数。runUserHandler函数将反向依次调用之前ServerAddHandler添加的处理函数。如果无用户自定义的处理函数,或者自定义的处理函数未处理,那么就将调用缺省处理函数。runUserHandler函数执行完毕后,processDataFromClient函数的最后一部分处理是向客户端返回数据。数据返回后再调用HTTPKeepalive函数决定是否要保留当前连接。














0 0
原创粉丝点击