使用 Nim 语言和使用 C 语言, 调用系统 POSIX 接口传递文件描述符的对比

691 查看

进程组之间传递文件描述符, 是多进程服务器共享套接字常用的方式. 基于此方式, 可以提供 round-robin , 使每个服务器获得平衡的负载.

Nim 和 C 语言调用相同的 POSIX 接口的编程方式如下, (事实上, Nim 是调用的 C POSIX 接口)

Nim

import posix

proc CMSG_LEN(length: cuint): cint {.importc, header: "<sys/socket.h>".}

proc socketpair(fds: var array[0..1, cint]): int {.inline.} =
    result = socketpair(AF_UNIX, SOCK_STREAM, 0, fds)

var 
    pipeFds: array[0 .. 1, cint]
    sockFds: array[0 .. 1, cint]

assert socketpair(pipeFds) == 0
assert socketpair(sockFds) == 0

####################### send model ###########################

var 
    sfd = pipeFds[0].SocketHandle()
    sDataBuff = ['\0']
    sIov = [TIOVec(iov_base: sDataBuff[0].addr(), iov_len: 1)]
    sCmsglen = CMSG_LEN(sizeof(SocketHandle).cuint())       # 20
    sCmsg = createU(Tcmsghdr, sCmsglen)
    sMsg = Tmsghdr(msg_name: nil, 
                   msg_namelen: 0,
                   msg_iov: sIov[0].addr(),
                   msg_iovlen: 1,
                   msg_control: sCmsg,
                   msg_controllen: sCmsglen.Socklen())

sCmsg.cmsg_len  = sCmsglen.Socklen()
sCmsg.cmsg_level = SOL_SOCKET
sCmsg.cmsg_type = SCM_RIGHTS
(cast[ptr int](CMSG_DATA(sCmsg)))[] = sockFds[0]

assert sfd.sendmsg(sMsg.addr(), 0) == 1
free(sCmsg)

####################### recv model ###########################

var 
    rfd = pipeFds[1].SocketHandle()
    rDataBuff = ['\0']
    rIov = [TIOVec(iov_base: rDataBuff[0].addr(), iov_len: 1)]
    rCmsglen = CMSG_LEN(sizeof(SocketHandle).cuint())       # 20
    rCmsg = createU(Tcmsghdr, rCmsglen)
    rMsg = Tmsghdr(msg_name: nil, 
                   msg_namelen: 0,
                   msg_iov: rIov[0].addr(),
                   msg_iovlen: 1,
                   msg_control: rCmsg,
                   msg_controllen: rCmsglen.Socklen())

assert rfd.recvmsg(rMsg.addr(), 0) == 1
assert rCmsg.cmsg_len == rCmsglen.Socklen()
assert rCmsg.cmsg_level == SOL_SOCKET
assert rCmsg.cmsg_type == SCM_RIGHTS
echo((cast[ptr int](CMSG_DATA(rCmsg)))[]) 

free(rCmsg)

C

#include <sys/socket.h>
#include <stdio.h>
#include <assert.h>
#include <malloc.h>

int main(int argc, char **argv) {
    int pipeFds[1];
    int sockFds[1];

    assert(socketpair(AF_UNIX, SOCK_STREAM, 0, pipeFds) == 0);
    assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockFds) == 0);

    /********************* send model *********************/

    struct msghdr sMsg;
    struct iovec sIov[1];
    char sDataBuff[1] = "";
    int sCmsglen = CMSG_LEN(sizeof(int));              // 20
    struct cmsghdr *sCmsg = malloc(sCmsglen);

    sIov[0].iov_base = sDataBuff;
    sIov[0].iov_len  = 1;

    sMsg.msg_name = NULL; 
    sMsg.msg_namelen = 0;
    sMsg.msg_iov = sIov;
    sMsg.msg_iovlen = 1;
    sMsg.msg_control = sCmsg;
    sMsg.msg_controllen = sCmsglen;                     // 20

    sCmsg->cmsg_len = sCmsglen;                         // 20
    sCmsg->cmsg_level = SOL_SOCKET;
    sCmsg->cmsg_type  = SCM_RIGHTS;
    *((int *)(CMSG_DATA(sCmsg))) = sockFds[0];

    assert(sendmsg(pipeFds[0], &sMsg, 0) == 1);
    free(sCmsg);

    /********************* recv model *********************/
    
    struct msghdr rMsg;
    struct iovec rIov[1];
    char rDataBuff[1] = "";
    int rCmsglen = CMSG_LEN(sizeof(int));               // 20
    struct cmsghdr *rCmsg = malloc(rCmsglen);

    rIov[0].iov_base = rDataBuff;
    rIov[0].iov_len  = 1;

    rMsg.msg_name = NULL; 
    rMsg.msg_namelen = 0;
    rMsg.msg_iov = rIov;
    rMsg.msg_iovlen = 1;
    rMsg.msg_control = rCmsg;
    rMsg.msg_controllen = rCmsglen;                     // 20

    assert(recvmsg(pipeFds[1], &rMsg, 0) == 1);
    
    assert(rCmsg->cmsg_len == rCmsglen);
    assert(rCmsg->cmsg_level == SOL_SOCKET);
    assert(rCmsg->cmsg_type == SCM_RIGHTS);
    printf("%d\n", *((int *)(CMSG_DATA(rCmsg))));       // 7

    free(rCmsg);
}