bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

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

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

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

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

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

  • 迭代服務(wù)器
  • 并發(fā)服務(wù)器,每個(gè)客戶請(qǐng)求fork一個(gè)子進(jìn)程
  • 預(yù)先派生子進(jìn)程,每個(gè)子進(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ù)器,每個(gè)客戶端請(qǐng)求創(chuàng)建一個(gè)線程
  • 預(yù)先創(chuàng)建線程服務(wù)器,使用互斥鎖上鎖保護(hù)accept
  • 預(yù)先創(chuàng)建線程服務(wù)器,由主線程調(diào)用accept

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

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

??一般來講,所有子進(jìn)程或線程都調(diào)用accept要比父進(jìn)程或主線程調(diào)用accept后將描述字傳遞個(gè)子進(jìn)程或線程來得快且簡(jiǎ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ù)模型的實(shí)現(xiàn)比傳遞文件描述符來得還簡(jiǎn)單一些。

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

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

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

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

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

#include <sys/types.h>
#include <sys/socket.h>

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

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

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

#include <sys/socket.h>

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

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

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

/* 傳入的參數(shù)length指的是一個(gè)控制信息元素(即一個(gè)cmsghdr結(jié)構(gòu)體)后面數(shù)據(jù)部分的字節(jié)數(shù),返回的是這個(gè)控制信息的總的字節(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ù)是一個(gè)控制信息中的數(shù)據(jù)部分的大小,返回的是這個(gè)根據(jù)這個(gè)數(shù)據(jù)部分大小,需要配置的cmsghdr結(jié)構(gòu)體中cmsg_len成員的值。這個(gè)大小將為對(duì)齊添加的填充數(shù)據(jù)也包含在內(nèi)。*/
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);

4. 文件描述符傳遞要點(diǎn)

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

??具體地說,為msghdr的成員msg_control分配一個(gè)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ā)送部分一樣,為控制信息配置好屬性,并在其后分配一個(gè)文件描述符的數(shù)據(jù)部分后,在成功調(diào)用recvmsg后,控制信息的數(shù)據(jù)部分就是在接收進(jìn)程中的新的文件描述符了,接收進(jìn)程可直接對(duì)該文件描述符進(jìn)行操作。

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

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

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

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

#include <sys/types.h>  
#include <sys/socket.h>  

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

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

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

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

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

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

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

/**
* @brief 發(fā)送文件描述符
* @param iSocketFd 由socketpair()創(chuàng)建的UNIX域套接字,用于傳遞文件描述符
* @param iSendFd 待發(fā)送的文件描述符
* @param iCodecType 通信通道編解碼類型
* @param pLogger 日志類指針
* @return errno 錯(cuò)誤碼
*/
int SocketChannel::SendChannelFd(int iSocketFd, int iSendFd, int iCodecType, std::shared_ptr<NetLogger> 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);
}

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

/**
* @brief 接收文件描述符
* @param iSocketFd 由socketpair()創(chuàng)建的UNIX域套接字,用于傳遞文件描述符
* @param iRecvFd 接收到的文件描述符
* @param iCodecType 接收到的通信通道編解碼類型
* @param pLogger 日志類指針
* @return errno 錯(cuò)誤碼
*/
int SocketChannel::RecvChannelFd(int iSocketFd, int& iRecvFd, int& iCodecType, std::shared_ptr<NetLogger> 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中的文件描述符傳遞實(shí)現(xiàn)。

### 6. Nginx文件描述符傳遞代碼實(shí)現(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域套接字傳遞文件描述符》。 如果覺得這篇文章對(duì)你有用,如果覺得Nebula框架還可以,幫忙到Nebula的[__Github__](https://github.com/Bwar/Nebula)或[__碼云__](https://gitee.com/Bwar/Nebula)給個(gè)star,謝謝。Nebula不僅是一個(gè)框架,還提供了一系列基于這個(gè)框架的應(yīng)用,目標(biāo)是打造一個(gè)高性能分布式服務(wù)集群解決方案。

參考資料:
* 《UNIX網(wǎng)絡(luò)編程》
* 《UNIX環(huán)境高級(jí)編程》
* [進(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)

本文標(biāo)題:通過UNIX域套接字傳遞文件描述符
本文地址:http://vcdvsql.cn/article6/gjehig.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站App開發(fā)企業(yè)網(wǎng)站制作網(wǎng)頁設(shè)計(jì)公司網(wǎng)站改版外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)