FreeSWITCH - mod_xml_rpc源码分析一socket_win.c


TChannel和TChanSwitch结构体

此文件内的函数基本上分成两类。一类的输入参数中包含TChannel结构体变量,另一类的输入参数中包含另一个结构体变量TChanSwitch。初看后觉得二者的差别很小,细看后发现差异在vtbl属性。vtbl这个结构体属性有很多函数指针属性。类似于监听函数(listen)、接受函数(accept)、读函数(read)和写函数(write)等等。TChannel包含读写等函数指针。TChanSwitch包含监听和接受函数指针。因此可以断定TChannel是用来代表一个连接上服务端的socket连接,TChanSwitch用来代表一个服务端socket。即,本文档内的函数分成两类:操作连接上服务端socket的函数,和操作服务端socket的函数。

struct TChannelVtbl {
    ChannelDestroyImpl            * destroy;
    ChannelWriteImpl              * write;
    ChannelReadImpl               * read;
    ChannelWaitImpl               * wait;
    ChannelInterruptImpl          * interrupt;
    ChannelFormatPeerInfoImpl     * formatPeerInfo;
};

struct _TChannel {
    uint                signature;
        /* With both background and foreground use of sockets, and
           background being both fork and pthread, it is very easy to
           screw up socket lifetime and try to destroy twice.  We use
           this signature to help catch such bugs.
        */
    void *              implP;
    struct TChannelVtbl vtbl;
};


struct TChanSwitchVtbl {
    SwitchDestroyImpl   * destroy;
    SwitchListenImpl    * listen;
    SwitchAcceptImpl    * accept;
    SwitchInterruptImpl * interrupt;
};

struct _TChanSwitch {
    uint                   signature;
        /* With both background and foreground use of switches, and
           background being both fork and pthread, it is very easy to
           screw up switch lifetime and try to destroy twice.  We use
           this signature to help catch such bugs.
        */
    void *                 implP;
    struct TChanSwitchVtbl vtbl;
};

TChannel

channelVtbl是个静态变量,保存了本文档内所有操作TChannel结构体的函数指针。

static struct TChannelVtbl const channelVtbl = {
    &channelDestroy,
    &channelWrite,
    &channelRead,
    &channelWait,
    &channelInterrupt,
    &channelFormatPeerInfo,
};

TChannel结构体内的implP属性是个无类型指针。意味着它可以指向任何类型。操作TChannel结构体的函数内都可以看到如下这句,将implP强制转换成socketWin结构体变量指针。

struct socketWin * const socketWinP = channelP->implP;


static void
channelDestroy(TChannel * const channelP) {

    struct socketWin * const socketWinP = channelP->implP;

    if (!socketWinP->userSuppliedWinsock)
        closesocket(socketWinP->winsock);

    CloseHandle(socketWinP->interruptEvent);

    free(socketWinP);
}

注意closesocket前的if判断语句。是否调用closesocket将依据userSuppliedWinsock这个布尔变量。变量名直译过来就是“用户提供Winsock”,再加入这个判断语句就是:如果不是用户提供Winsock,则调用closesocket。这样一来也好理解,如果socket句柄是由abyss库外部提供,即这里提到的“用户”,那么关闭socket句柄理应由库本身来做。


这个函数将依据传入的SOCKET值创建一个TChannel变量。此文档名为socket_win.c,即它是abyss库在windows平台下的特殊实现。之前也提到过TChannel还有个指向无类型的指针。在windows平台下,这个无类型指针将指向socketWin结构体。因此,这个函数还会为TChannel创建创建一个socketWin变量并赋给TChannel。在此函数内又看到了userSuppliedWinsock,在这里可以看到此属性恒为TRUE。即,SOCKET值都是由外部创建提供的。

static void
makeChannelFromWinsock(SOCKET        const winsock,
                       TChannel **   const channelPP,
                       const char ** const errorP) {

    struct socketWin * socketWinP;

    MALLOCVAR(socketWinP);
    
    if (socketWinP == NULL)
        xmlrpc_asprintf(errorP, "Unable to allocate memory for Windows "
                        "socket descriptor");
    else {
        TChannel * channelP;
        
        socketWinP->winsock = winsock;
        socketWinP->userSuppliedWinsock = TRUE;
        socketWinP->interruptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        ChannelCreate(&channelVtbl, socketWinP, &channelP);
        
        if (channelP == NULL)
            xmlrpc_asprintf(errorP, "Unable to allocate memory for "
                            "channel descriptor.");
        else {
            *channelPP = channelP;
            *errorP = NULL;
        }
        if (*errorP) {
            CloseHandle(socketWinP->interruptEvent);
            free(socketWinP);
        }
    }
}

函数内还调用了另一个文件内的函数ChannelCreate,此函数在channel.c文件内。这个函数很简单,就是申请内容创建一个TChannel变量,然后将静态变量channelVtbl和socketWinP变量赋给TChannel变量。如果一切正常,创建好后的TChannel内将包含一个socketWin变量,vtbl结构体属性内的函数指针将指向本文档内的各个静态函数。


makeChannelInfo函数只是创建了一个abyss_win_chaninfo变量。

static void
makeChannelInfo(struct abyss_win_chaninfo ** const channelInfoPP,
                struct sockaddr              const peerAddr,
                socklen_t                    const peerAddrLen,
                const char **                const errorP) {

    struct abyss_win_chaninfo * channelInfoP;

    MALLOCVAR(channelInfoP);
    
    if (channelInfoP == NULL)
        xmlrpc_asprintf(errorP, "Unable to allocate memory");
    else {
        channelInfoP->peerAddrLen = peerAddrLen;
        channelInfoP->peerAddr    = peerAddr;
        
        *channelInfoPP = channelInfoP;

        *errorP = NULL;
    }
}


ChannelWinCreateWinsock函数包含了上述两个函数。此函数的输入参数其实就是一个SOCKET值。可以理解为外部生成了一个SOCKET句柄后,此函数依据生成好的SOCKET句柄,再创建两个结构体变量。一个是TChannel,另一个是abyss_win_chaninfo。这个函数将依据SOCKET句柄得到的sockaddr变量放置在abyss_win_chaninfo结构体变量内。SOCKET句柄保存在TChannel结构体变量内,且还保存了被封装后的windows平台下的socket函数指针。

void
ChannelWinCreateWinsock(SOCKET                       const fd,
                        TChannel **                  const channelPP,
                        struct abyss_win_chaninfo ** const channelInfoPP,
                        const char **                const errorP) {

    struct sockaddr peerAddr;
    socklen_t peerAddrLen;
    int rc;

    peerAddrLen = sizeof(peerAddr);

    rc = getpeername(fd, &peerAddr, &peerAddrLen);

    if (rc != 0) {
        int const lastError = WSAGetLastError();
        if (lastError == WSAENOTCONN) {
            /* NOTE: This specific string 'not in connected' is
               required by one of the rpctest suite items, in abyss.c
               (line 186), hence the separation of the error messages
               in this case ...
            */
            xmlrpc_asprintf(errorP, "Socket on file descriptor %d "
                            "is not in connected state. WSAERROR = %d (%s)",
                            fd, lastError, getWSAError(lastError));
        } else
            xmlrpc_asprintf(errorP, "getpeername() failed. WSAERROR = %d (%s)",
                        lastError, getWSAError(lastError));
    } else {
        makeChannelInfo(channelInfoPP, peerAddr, peerAddrLen, errorP);
        if (!*errorP) {
            makeChannelFromWinsock(fd, channelPP, errorP);

            if (*errorP)
                free(*channelInfoPP);
        }
    }
}


TChanSwitch

createChannelForAccept函数的作用感觉和ChannelWinCreateWinsock函数一样。只不过函数签名不太一样,增加了一个输入参数。

static void
createChannelForAccept(int             const acceptedWinsock,
                       struct sockaddr const peerAddr,
                       TChannel **     const channelPP,
                       void **         const channelInfoPP,
                       const char **   const errorP) {

    struct abyss_win_chaninfo * channelInfoP;
    makeChannelInfo(&channelInfoP, peerAddr, sizeof(peerAddr), errorP);
    if (!*errorP) {
        struct socketWin * acceptedSocketP;

        MALLOCVAR(acceptedSocketP);

        if (!acceptedSocketP)
            xmlrpc_asprintf(errorP, "Unable to allocate memory");
        else {
            TChannel * channelP;

            acceptedSocketP->winsock             = acceptedWinsock;
            acceptedSocketP->userSuppliedWinsock = FALSE;
            acceptedSocketP->interruptEvent      =
                CreateEvent(NULL, FALSE, FALSE, NULL);

            ChannelCreate(&channelVtbl, acceptedSocketP, &channelP);
            if (!channelP)
                xmlrpc_asprintf(errorP,
                                "Failed to create TChannel object.");
            else {
                *errorP        = NULL;
                *channelPP     = channelP;
                *channelInfoPP = channelInfoP;
            }
            if (*errorP) {
                CloseHandle(acceptedSocketP->interruptEvent);
                free(acceptedSocketP);
            }
        }
    }
}

ChanSwitchWinCreate和ChanSwitchWinCreateWinsock两个函数的作用相同,都是为了创建一个TChanSwitch变量。只是二者的函数签名不一样,前一个的输入参数是端口号,后一个的输入参数是SOCKET句柄。在这两个函数内也能看到,chanSwitchVtbl全局静态变量赋给了TChanSwitch变量的vtbl属性。这个手法和TChannel结构体一致。


其他

之前一直提到的socketWin结构体是在文件的最开始部分声明的。此结构体保存SOCKET句柄、一个布尔型变量(指示SOCKET句柄由谁创建)以及一个windows的句柄。windows句柄在创建每个socketWin结构体变量时生成,现在还不清楚它的用途。

struct socketWin {
/*----------------------------------------------------------------------------
   The properties/state of a TSocket unique to a Unix TSocket.
-----------------------------------------------------------------------------*/
    SOCKET winsock;
    bool userSuppliedWinsock;
        /* 'socket' was supplied by the user; it belongs to him */
    HANDLE interruptEvent;
};
SocketWinInit和SocketWinTerm分别是winsock初始化和卸载函数。connected函数用来判断一个SOCKET句柄是否处于连接状态。getWSAError函数返回错误代码对应的错误字串。



你可能感兴趣的:(freeswitch,abyss)