真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

通過UNIX域套接字傳遞文件描述符

??傳送文件描述符是高并發(fā)網(wǎng)絡(luò)服務(wù)編程的一種常見實現(xiàn)方式。Nebula 高性能通用網(wǎng)絡(luò)框架即采用了UNIX域套接字傳遞文件描述符設(shè)計和實現(xiàn)。本文詳細(xì)說明一下傳送文件描述符的應(yīng)用。

創(chuàng)新互聯(lián)自2013年創(chuàng)立以來,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都網(wǎng)站設(shè)計、網(wǎng)站制作、外貿(mào)營銷網(wǎng)站建設(shè)網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元掇刀做網(wǎng)站,已為上家服務(wù),為掇刀各地企業(yè)和個人服務(wù),聯(lián)系電話:028-86922220

1. TCP服務(wù)器程序設(shè)計范式

??開發(fā)一個服務(wù)器程序,有較多的的程序設(shè)計范式可供選擇,不同范式有其自身的特點和實用范圍,明了不同范式的特性有助于我們服務(wù)器程序的開發(fā)。常見的TCP服務(wù)器程序設(shè)計范式有以下幾種:

  • 迭代服務(wù)器
  • 并發(fā)服務(wù)器,每個客戶請求fork一個子進(jìn)程
  • 預(yù)先派生子進(jìn)程,每個子進(jìn)程無保護(hù)地調(diào)用accept
  • 預(yù)先派生子進(jìn)程,使用文件上鎖保護(hù)accept
  • 預(yù)先派生子進(jìn)程,使用線程互斥鎖上鎖保護(hù)accept
  • 預(yù)先派生子進(jìn)程,父進(jìn)程向子進(jìn)程傳遞套接字描述符
  • 并發(fā)服務(wù)器,每個客戶端請求創(chuàng)建一個線程
  • 預(yù)先創(chuàng)建線程服務(wù)器,使用互斥鎖上鎖保護(hù)accept
  • 預(yù)先創(chuàng)建線程服務(wù)器,由主線程調(diào)用accept

??當(dāng)系統(tǒng)負(fù)載較輕時,傳統(tǒng)的并發(fā)服務(wù)器程序模型就夠了。相對于傳統(tǒng)的每個客戶一次fork設(shè)計,預(yù)先創(chuàng)建一個進(jìn)程池或線程池可以減少進(jìn)程控制CPU時間,大約可減少10倍以上。

??某些實現(xiàn)允許多個子進(jìn)程或線程阻塞在accept上,然而在另一些實現(xiàn)中,我們必須使用文件鎖、線程互斥鎖或其他類型的鎖來確保每次只有一個子進(jìn)程或線程在accept。

??一般來講,所有子進(jìn)程或線程都調(diào)用accept要比父進(jìn)程或主線程調(diào)用accept后將描述字傳遞個子進(jìn)程或線程來得快且簡單。

2. Nebula 為什么采用傳遞文件描述符方式?

??Nebula框架是預(yù)先創(chuàng)建多進(jìn)程,由Manager主進(jìn)程accept后傳遞文件描述符到Worker子進(jìn)程的服務(wù)模型(Nebula進(jìn)程模型)。為什么不采用像nginx那樣多線程由子線程使用互斥鎖上鎖保護(hù)accept的服務(wù)模型?而且這種服務(wù)模型的實現(xiàn)比傳遞文件描述符來得還簡單一些。

??Nebula框架采用無鎖設(shè)計,進(jìn)程之前完全不共享數(shù)據(jù),不存在需要互斥訪問的地方。沒錯,會存在數(shù)據(jù)多副本問題,但這些多副本往往只是些配置數(shù)據(jù),占用不了太大內(nèi)存,與加鎖解鎖帶來的代碼復(fù)雜度及鎖開銷相比這點內(nèi)存代價更劃算也更簡單。

??同一個Nebula服務(wù)的工作進(jìn)程間不相互通信,采用進(jìn)程和線程并無太大差異,之所以采用進(jìn)程而不是線程的最重要考慮是Nebula是出于穩(wěn)定性和容錯性考慮。Nebula是通用框架,完全業(yè)務(wù)無關(guān),業(yè)務(wù)都是通過動態(tài)加載的方式或通過將Nebula鏈接進(jìn)業(yè)務(wù)Server的方式來實現(xiàn)。Nebula框架無法預(yù)知業(yè)務(wù)代碼的質(zhì)量,但可以保證在服務(wù)因業(yè)務(wù)代碼導(dǎo)致coredump或其他情況時,框架可以實時監(jiān)控到并立刻拉起服務(wù)進(jìn)程,最大程度保障服務(wù)可用性。

??決定Nebula采用傳遞文件描述符方式的最重要一點是:Nebula定位是高性能分布式服務(wù)集群解決方案的基礎(chǔ)通信框架,其設(shè)計更多要為構(gòu)建分布式服務(wù)集群而考慮。集群不同服務(wù)節(jié)點之間通過TCP通信,而所有邏輯都是Worker進(jìn)程負(fù)責(zé),這意味著節(jié)點之間通信需要指定到Worker進(jìn)程,而如果采用子進(jìn)程競爭accept的方式無法保證指定的子進(jìn)程獲得資源,那么第一個通信數(shù)據(jù)包將會路由錯誤。采用傳遞文件描述符方式可以很完美地解決這個問題,而且傳遞文件描述符也非常高效。

3. 文件描述符傳遞函數(shù)和數(shù)據(jù)結(jié)構(gòu)

??文件描述符傳遞通過調(diào)用sendmsg()函數(shù)發(fā)送,調(diào)用recvmsg()函數(shù)接收:

#include 
#include 

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

??這兩個函數(shù)與sendto和recvfrom函數(shù)相似,只不過可以傳輸更復(fù)雜的數(shù)據(jù)結(jié)構(gòu),不僅可以傳輸一般數(shù)據(jù),還可以傳輸額外的數(shù)據(jù),即文件描述符。下面來看結(jié)構(gòu)體msghdr及其相關(guān)結(jié)構(gòu)體 :

struct msghdr {
    void         *msg_name;       /* optional address */
    socklen_t     msg_namelen;    /* size of address */
    struct iovec *msg_iov;        /* scatter/gather array */
    size_t        msg_iovlen;     /* # elements in msg_iov */
    void         *msg_control;    /* ancillary data, see below */
    size_t        msg_controllen; /* ancillary data buffer len */
    int           msg_flags;      /* flags on received message */
};

/* iovec結(jié)構(gòu)體 */
struct iovec {
    void  *iov_base;    /* Starting address */
    size_t iov_len;     /* Number of bytes to transfer */
};

/* cmsghdr結(jié)構(gòu)體 */
struct cmsghdr {
    socklen_t cmsg_len;    /* data byte count, including header */
    int       cmsg_level;  /* originating protocol */
    int       cmsg_type;   /* protocol-specific type */
    /* followed by unsigned char cmsg_data[]; */
};

??msghdr結(jié)構(gòu)成員說明:

  • msg_name :即對等方的地址指針,不關(guān)心時設(shè)為NULL即可.
  • msg_namelen:地址長度,不關(guān)心時設(shè)置為0即可.
  • msg_iov:結(jié)構(gòu)體iovec指針; iovec的成員iov_base 可以認(rèn)為是傳輸正常數(shù)據(jù)時的buf,iov_len 是buf 的大小。
  • msg_iovlen:iovec類型的元素的個數(shù),每一個緩沖區(qū)的起始地址和大小由iovec類型自包含。當(dāng)有n個iovec結(jié)構(gòu)體時,此值為n。
  • msg_control:是一個指向cmsghdr 結(jié)構(gòu)體的指針,用來發(fā)送或接收控制信息。
  • msg_controllen :控制信息所占用的字節(jié)數(shù)。注意,msg_controllen與前面的msg_iovlen不同,msg_iovlen是指的由成員msg_iov所指向的iovec型的數(shù)組的元素個數(shù),而msg_controllen,則是所有控制信息所占用的總的字節(jié)數(shù)。
  • msg_flags : 用來描述接受到的消息的性質(zhì),由調(diào)用recvmsg時傳入的flags參數(shù)設(shè)置。

??為了對齊,可能存在一些填充字節(jié),跟不同系統(tǒng)的實現(xiàn)有關(guān)控制信息的數(shù)據(jù)部分,是直接存儲在cmsghdr結(jié)構(gòu)體的cmsg_type之后的。但中間可能有一些由于對齊產(chǎn)生的填充字節(jié),由于這些填充數(shù)據(jù)的存在,對于這些控制數(shù)據(jù)的訪問,必須使用Linux提供的一些專用宏來完成:

#include 

/* 返回msgh所指向的msghdr類型的緩沖區(qū)中的第一個cmsghdr結(jié)構(gòu)體的指針。*/
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);

/* 返回傳入的cmsghdr類型的指針的下一個cmsghdr結(jié)構(gòu)體的指針。 */
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);

/* 根據(jù)傳入的length大小,返回一個包含了添加對齊作用的填充數(shù)據(jù)后的大小。 */
size_t CMSG_ALIGN(size_t length);

/* 傳入的參數(shù)length指的是一個控制信息元素(即一個cmsghdr結(jié)構(gòu)體)后面數(shù)據(jù)部分的字節(jié)數(shù),返回的是這個控制信息的總的字節(jié)數(shù),即包含了頭部(即cmsghdr各成員)、數(shù)據(jù)部分和填充數(shù)據(jù)的總和。*/
size_t CMSG_SPACE(size_t length);

/* 根據(jù)傳入的cmsghdr指針參數(shù),返回其后面數(shù)據(jù)部分的指針。*/
size_t CMSG_LEN(size_t length);

/* 傳入的參數(shù)是一個控制信息中的數(shù)據(jù)部分的大小,返回的是這個根據(jù)這個數(shù)據(jù)部分大小,需要配置的cmsghdr結(jié)構(gòu)體中cmsg_len成員的值。這個大小將為對齊添加的填充數(shù)據(jù)也包含在內(nèi)。*/
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);

4. 文件描述符傳遞要點

??sendmsg提供了可以傳遞控制信息的功能,要實現(xiàn)的傳遞描述符這一功能必須要用到這個控制信息。在msghdr變量的cmsghdr成員中,由控制頭cmsg_level和cmsg_type來設(shè)置傳遞文件描述符這一屬性,并將要傳遞的文件描述符作為數(shù)據(jù)部分,保存在cmsghdr變量的后面。這樣就可以實現(xiàn)傳遞文件描述符這一功能,這種情況是不需要使用msg_iov來傳遞數(shù)據(jù)的。

??具體地說,為msghdr的成員msg_control分配一個cmsghdr的空間,將該cmsghdr結(jié)構(gòu)的cmsg_level設(shè)置為SOL_SOCKET,cmsg_type設(shè)置為SCM_RIGHTS,并將要傳遞的文件描述符作為數(shù)據(jù)部分,調(diào)用sendmsg即可。其中SCM表示socket-level control message,SCM_RIGHTS表示我們要傳遞訪問權(quán)限。

??跟發(fā)送部分一樣,為控制信息配置好屬性,并在其后分配一個文件描述符的數(shù)據(jù)部分后,在成功調(diào)用recvmsg后,控制信息的數(shù)據(jù)部分就是在接收進(jìn)程中的新的文件描述符了,接收進(jìn)程可直接對該文件描述符進(jìn)行操作。

??文件描述符傳遞并不是將文件描述符數(shù)字傳遞,而是文件描述符對應(yīng)數(shù)據(jù)結(jié)構(gòu)。在主進(jìn)程accept的到的文件描述符7傳遞到子進(jìn)程后文件描述符有可能是7,更有可能是7以外的其他數(shù)值,但無論是什么數(shù)值并不重要,重要的是傳遞之后的連接跟傳遞之前的連接是同一個連接。

??通常在完成文件描述符傳遞后,接收進(jìn)程接管文件描述符,發(fā)送進(jìn)程則應(yīng)調(diào)用close關(guān)閉已傳遞的文件描述符。發(fā)送進(jìn)程關(guān)閉描述符并不造成關(guān)閉該文件或設(shè)備,因為該描述符對應(yīng)的文件仍被視為由接收者進(jìn)程打開(即使接收進(jìn)程尚未接收到該描述符)。

??文件描述符傳遞可經(jīng)由基于STREAMS的管道,也可經(jīng)由UNIX域套接字。兩種方式在《UNIX網(wǎng)絡(luò)編程》中均有描述,Nebula采用的UNIX域套接字傳遞文件描述符。

??創(chuàng)建用于傳遞文件描述符的UNIX域套接字用到socketpair函數(shù):

#include   
#include   

int socketpair(int d, int type, int protocol, int sv[2]);  

??傳入的參數(shù)sv為一個整型數(shù)組,有兩個元素。當(dāng)調(diào)用成功后,這個數(shù)組的兩個元素即為2個文件描述符。一對連接起來的Unix匿名域套接字就建立起來了,它們就像一個全雙工的管道,每一端都既可讀也可寫。

5. Nebula框架中傳遞文件描述符的實現(xiàn)

??Nebula框架的文件描述符屬于SocketChannel的基本屬性,文件描述符傳遞方法是SocketChannel的靜態(tài)方法。

文件描述符傳遞方法聲明:

static int SendChannelFd(int iSocketFd, int iSendFd, int iCodecType, std::shared_ptr pLogger);
static int RecvChannelFd(int iSocketFd, int& iRecvFd, int& iCodecType, std::shared_ptr pLogger);

文件描述符發(fā)送方法實現(xiàn):

/**
* @brief 發(fā)送文件描述符
* @param iSocketFd 由socketpair()創(chuàng)建的UNIX域套接字,用于傳遞文件描述符
* @param iSendFd 待發(fā)送的文件描述符
* @param iCodecType 通信通道編解碼類型
* @param pLogger 日志類指針
* @return errno 錯誤碼
*/
int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iCodecType, std::shared_ptr pLogger)
{
    ssize_t             n;
    struct iovec        iov[1];
    struct msghdr       msg;
    tagChannelCtx stCh;
    int iError = 0;

    stCh.iFd = iSendFd;
    stCh.iCodecType = iCodecType;

    union
    {
        struct cmsghdr  cm;
        char            space[CMSG_SPACE(sizeof(int))];
    } cmsg;

    if (stCh.iFd == -1)
    {
        msg.msg_control = NULL;
        msg.msg_controllen = 0;

    }
    else
    {
        msg.msg_control = (caddr_t) &cmsg;
        msg.msg_controllen = sizeof(cmsg);

        memset(&cmsg, 0, sizeof(cmsg));

        cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
        cmsg.cm.cmsg_level = SOL_SOCKET;
        cmsg.cm.cmsg_type = SCM_RIGHTS;

        *(int *) CMSG_DATA(&cmsg.cm) = stCh.iFd;
    }

    msg.msg_flags = 0;

    iov[0].iov_base = (char*)&stCh;
    iov[0].iov_len = sizeof(tagChannelCtx);

    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    n = sendmsg(iSocketFd, &msg, 0);

    if (n == -1)
    {
        pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "sendmsg() failed, errno %d", errno);
        iError = (errno == 0) ? ERR_TRANSFER_FD : errno;
        return(iError);
    }

    return(ERR_OK);
}

文件描述符接收方法實現(xiàn):

/**
* @brief 接收文件描述符
* @param iSocketFd 由socketpair()創(chuàng)建的UNIX域套接字,用于傳遞文件描述符
* @param iRecvFd 接收到的文件描述符
* @param iCodecType 接收到的通信通道編解碼類型
* @param pLogger 日志類指針
* @return errno 錯誤碼
*/
int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iCodecType, std::shared_ptr pLogger)
{
    ssize_t             n;
    struct iovec        iov[1];
    struct msghdr       msg;
    tagChannelCtx stCh;
    int iError = 0;

    union {
        struct cmsghdr  cm;
        char            space[CMSG_SPACE(sizeof(int))];
    } cmsg;

    iov[0].iov_base = (char*)&stCh;
    iov[0].iov_len = sizeof(tagChannelCtx);

    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    msg.msg_control = (caddr_t) &cmsg;
    msg.msg_controllen = sizeof(cmsg);

    n = recvmsg(iSocketFd, &msg, 0);

    if (n == -1) {
        pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() failed, errno %d", errno);
        iError = (errno == 0) ? ERR_TRANSFER_FD : errno;
        return(iError);
    }

    if (n == 0) {
        pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() return zero, errno %d", errno);
        iError = (errno == 0) ? ERR_TRANSFER_FD : errno;
        return(ERR_CHANNEL_EOF);
    }

    if ((size_t) n < sizeof(tagChannelCtx))
    {
        pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "rrecvmsg() returned not enough data: %z, errno %d", n, errno);
        iError = (errno == 0) ? ERR_TRANSFER_FD : errno;
        return(iError);
    }

    if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int)))
    {
        pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() returned too small ancillary data");
        iError = (errno == 0) ? ERR_TRANSFER_FD : errno;
        return(iError);
    }

    if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS)
    {
        pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__,
                        "recvmsg() returned invalid ancillary data level %d or type %d", cmsg.cm.cmsg_level, cmsg.cm.cmsg_type);
        iError = (errno == 0) ? ERR_TRANSFER_FD : errno;
        return(iError);
    }

    stCh.iFd = *(int *) CMSG_DATA(&cmsg.cm);

    if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC))
    {
        pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "recvmsg() truncated data");
        iError = (errno == 0) ? ERR_TRANSFER_FD : errno;
        return(iError);
    }

    iRecvFd = stCh.iFd;
    iCodecType = stCh.iCodecType;

    return(ERR_OK);
}

Manager進(jìn)程的void Manager::CreateWorker()方法創(chuàng)建用于傳遞文件描述符的UNIX域套接字:

int iControlFds[2];
int iDataFds[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, iControlFds) < 0)
{
    LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024));
}
if (socketpair(PF_UNIX, SOCK_STREAM, 0, iDataFds) < 0)
{
    LOG4_ERROR("error %d: %s", errno, strerror_r(errno, m_szErrBuff, 1024));
}

Manager進(jìn)程發(fā)送文件描述符:

int iCodec = m_stManagerInfo.eCodec;    // 將編解碼方式和文件描述符一同發(fā)送給Worker進(jìn)程
int iErrno = SocketChannel::SendChannelFd(worker_pid_fd.second, iAcceptFd, iCodec, m_pLogger);
if (iErrno == 0)
{
    AddWorkerLoad(worker_pid_fd.first);
}
else
{
    LOG4_ERROR("error %d: %s", iErrno, strerror_r(iErrno, m_szErrBuff, 1024));
}
close(iAcceptFd);   // 發(fā)送完畢,關(guān)閉文件描述符

Worker進(jìn)程接收文件描述符:

``` C++
int iAcceptFd = -1;
int iCodec = 0; // 這里的編解碼方式在RecvChannelFd方法中獲得
int iErrno = SocketChannel::RecvChannelFd(m_stWorkerInfo.iManagerDataFd, iAcceptFd, iCodec, m_pLogger);


??至此,Nebula框架的文件描述符傳遞分享完畢,下面再看看nginx中的文件描述符傳遞實現(xiàn)。

### 6. Nginx文件描述符傳遞代碼實現(xiàn)
??Nginx的文件描述符傳遞代碼在os/unix/ngx_channel.c文件中。

nginx中發(fā)送文件描述符代碼:

ngx_int_t
ngx_write_channel(ngx_socket_t s, ngx_channel_t ch, size_t size,
ngx_log_t
log)
{
ssize_t n;
ngx_err_t err;
struct iovec iov[1];
struct msghdr msg;

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)

union {
    struct cmsghdr  cm;
    char            space[CMSG_SPACE(sizeof(int))];
} cmsg;

if (ch->fd == -1) {
    msg.msg_control = NULL;
    msg.msg_controllen = 0;

} else {
    msg.msg_control = (caddr_t) &cmsg;
    msg.msg_controllen = sizeof(cmsg);

    ngx_memzero(&cmsg, sizeof(cmsg));

    cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
    cmsg.cm.cmsg_level = SOL_SOCKET;
    cmsg.cm.cmsg_type = SCM_RIGHTS;

    /*
     * We have to use ngx_memcpy() instead of simple
     *   *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;
     * because some gcc 4.4 with -O2/3/s optimization issues the warning:
     *   dereferencing type-punned pointer will break strict-aliasing rules
     *
     * Fortunately, gcc with -O1 compiles this ngx_memcpy()
     * in the same simple assignment as in the code above
     */

    ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int));
}

msg.msg_flags = 0;

#else

if (ch->fd == -1) {
    msg.msg_accrights = NULL;
    msg.msg_accrightslen = 0;

} else {
    msg.msg_accrights = (caddr_t) &ch->fd;
    msg.msg_accrightslen = sizeof(int);
}

#endif

iov[0].iov_base = (char *) ch;
iov[0].iov_len = size;

msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;

n = sendmsg(s, &msg, 0);

if (n == -1) {
    err = ngx_errno;
    if (err == NGX_EAGAIN) {
        return NGX_AGAIN;
    }

    ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed");
    return NGX_ERROR;
}

return NGX_OK;

}


nginx中接收文件描述符代碼:

ngx_int_t
ngx_read_channel(ngx_socket_t s, ngx_channel_t ch, size_t size, ngx_log_t log)
{
ssize_t n;
ngx_err_t err;
struct iovec iov[1];
struct msghdr msg;

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
union {
struct cmsghdr cm;
char space[CMSG_SPACE(sizeof(int))];
} cmsg;
#else
int fd;
#endif

iov[0].iov_base = (char *) ch;
iov[0].iov_len = size;

msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
msg.msg_control = (caddr_t) &cmsg;
msg.msg_controllen = sizeof(cmsg);
#else
msg.msg_accrights = (caddr_t) &fd;
msg.msg_accrightslen = sizeof(int);
#endif

n = recvmsg(s, &msg, 0);

if (n == -1) {
    err = ngx_errno;
    if (err == NGX_EAGAIN) {
        return NGX_AGAIN;
    }

    ngx_log_error(NGX_LOG_ALERT, log, err, "recvmsg() failed");
    return NGX_ERROR;
}

if (n == 0) {
    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "recvmsg() returned zero");
    return NGX_ERROR;
}

if ((size_t) n < sizeof(ngx_channel_t)) {
    ngx_log_error(NGX_LOG_ALERT, log, 0,
                  "recvmsg() returned not enough data: %z", n);
    return NGX_ERROR;
}

#if (NGX_HAVE_MSGHDR_MSG_CONTROL)

if (ch->command == NGX_CMD_OPEN_CHANNEL) {

    if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) {
        ngx_log_error(NGX_LOG_ALERT, log, 0,
                      "recvmsg() returned too small ancillary data");
        return NGX_ERROR;
    }

    if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS)
    {
        ngx_log_error(NGX_LOG_ALERT, log, 0,
                      "recvmsg() returned invalid ancillary data "
                      "level %d or type %d",
                      cmsg.cm.cmsg_level, cmsg.cm.cmsg_type);
        return NGX_ERROR;
    }

    /* ch->fd = *(int *) CMSG_DATA(&cmsg.cm); */

    ngx_memcpy(&ch->fd, CMSG_DATA(&cmsg.cm), sizeof(int));
}

if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
    ngx_log_error(NGX_LOG_ALERT, log, 0,
                  "recvmsg() truncated data");
}

#else

if (ch->command == NGX_CMD_OPEN_CHANNEL) {
    if (msg.msg_accrightslen != sizeof(int)) {
        ngx_log_error(NGX_LOG_ALERT, log, 0,
                      "recvmsg() returned no ancillary data");
        return NGX_ERROR;
    }

    ch->fd = fd;
}

#endif

return n;

}



??__Nebula框架系列技術(shù)分享__ 之 《通過UNIX域套接字傳遞文件描述符》。 如果覺得這篇文章對你有用,如果覺得Nebula框架還可以,幫忙到Nebula的[__Github__](https://github.com/Bwar/Nebula)或[__碼云__](https://gitee.com/Bwar/Nebula)給個star,謝謝。Nebula不僅是一個框架,還提供了一系列基于這個框架的應(yīng)用,目標(biāo)是打造一個高性能分布式服務(wù)集群解決方案。

參考資料:
* 《UNIX網(wǎng)絡(luò)編程》
* 《UNIX環(huán)境高級編程》
* [進(jìn)程間傳遞文件描述符](https://pureage.info/2015/03/19/passing-file-descriptors.html)
* [linux網(wǎng)絡(luò)編程之socket(十六):通過UNIX域套接字傳遞描述符和 sendmsg/recvmsg 函數(shù)](https://blog.csdn.net/jnu_simba/article/details/9079627)

分享名稱:通過UNIX域套接字傳遞文件描述符
URL分享:http://weahome.cn/article/gjehig.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部