원본글 주소 : http://blog.naver.com/endfirst/30029848638

원본 c++ -> c언어로 변경

현재  c언어로 변경된 아래 소스는 리눅스에서 잘 작동된다.

 

fd는 fd(file descriptor)를 가리킴.

--원본글---

 MSDN에는 WSASendMsg라는 녀석을 준비하였는데 대충 비슷하게 보인다. 다만 Overlapped I/O를 Windows용으로 써야하기때문에 API가 다른 것 같다.

socket에는 sendmsg/recvmsg라는 녀석이 있다. 이 녀석 형태를 보면 아래와 같다.

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

send/recv와 달리 버퍼가 안 보이고, msghdr 라는 구조체를 쓰는데 - 물론 이 녀석이 버퍼겠지 - 이걸 우선 까보자.
struct msghdr {
    void         * msg_name;     // 접속할 주소
    socklen_t    msg_namelen;    // 접속할 주소 크기
    struct iovec * msg_iov;      // IO 버퍼
    size_t       msg_iovlen;     // IO 버퍼 개수
    void         * msg_control;  // 제어 정보 버퍼
    socklen_t    msg_controllen; // 제어 정보 버퍼 크기
    int          msg_flags;      // 플래그
};
읔, 뭔가 많다. ㅡ_-) 난 이런게 젤 싫더라...

sendmsg/recvmsg 는 접속/비접속형 소켓을 모두 지원한다. msg_name과 msg_namelen은 비접속형 소켓(예:UDP)에서 받을 주소 구조체(예:struct sockaddr_in)을 알맞게 만들어서 넣어주는 것이다.

또한 I/O vector를 지원하여, msg_iov에는 struct iovec 배열의 주소, msg_iovlen은 배열 원소 개수를 예쁘게 넣는다. 이때 msg_iov는 NULL이 아니며, msg_iovlen은 0이 될 수 없다. 무조건 뭔가라도 하나 보내야한다. 만약 FD외에도 다른 데이터를 함께 넘기고 싶다면 이것을 애용(?)하면 좋겠다.

자, 드디어 FD에 대한 중요한 내용. 제어 정보 - 즉, FD 같은 걸 넘기는 방법이다. 이것을 위해 준비된 구조체가 있다. 바로 cmsghdr라는 구조체인데, 역시나 한 번 까보자.

struct cmsghdr {
    socklen_t   cmsg_len;   // 제어 정보 전체 길이 (헤더크기 포함)
    int         cmsg_level; // 제어 정보 레벨
    int         cmsg_type;  // 제어 정보 타입
    /*
       unsigned char cmsg_data[???];   // 제어 정보
    */
};

요 놈은 제어 정보 헤더이다. 넘기고 싶은게 있다면, 이 헤더를 이용해서 새로운 구조체를 만들어야한다. 각 인자를 설명하는 것보다 대충 먼저 예제를 만들어보자. 우리가 넘기고자 하는 것은 file-descriptor. int형이고, 32비트 운영체제에서 4바이트를 차지하는 어여쁜 녀석이다. 이 녀석을 넘길 데이터 구조체를 만들어보자. UNP에선 union을 썼는데, 귀찮으니 대충 보고 따라해보자.

참고로 FD를 넘기려면 Protocol은 UNIX domain, level은 SOL_SOCKET, type은 SCM_RIGHTS로 설정하라고 위대하신 UNP님께서 말씀하신다.

typedef union _cmsg_fd
{
    struct cmsghdr cmsg;
    char cmsg[CMSG_SPACE(sizeof(int))];
} cmsg_fd;

CMSG_SPACE 라는 매크로는 (제어 정보 + sizeof(cmsghdr))이다. 결국 cmsg_fd = [ cmsghdr ][ int ] 로 이뤄진 것이다. 우리가 넘길 건 int 하나이니 대충 이렇게 잡고 넘어가자. 나중에 데이터부분을 접근하려면 CMSG_DATA(cmsg_fd)하면 데이터부분만 포인터로 접근할 수 있다.

자, 이제 예제 소스이다. _-_

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/uio.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <errno.h>


typedef union _cmsg_fd{
    struct cmsghdr cmsg;
    char data[CMSG_SPACE(sizeof(int))];
} cmsg_fd;

int recvFD(int sockfd, int* fd, char* buf, size_t buflen);
int sendFD(int sockfd, int fd, const char* buf, size_t buflen);

int main( int argc, char* argv[] ){
    int sockfd[2];
    if( -1 == socketpair(AF_UNIX, SOCK_STREAM, AF_LOCAL, sockfd))
    {
        printf("err:%d\n",getpid());
        return -1;       
    }
    if( fork())
    {
        int fd=1;
        sendFD(sockfd[0], fd, (char*)&fd, sizeof(fd));
        close(1);
        printf("standard output is closed\n");
        int res;
        wait(&res);
    }else{
        close(1);
        int fd, ofd;
        recvFD(sockfd[1],&fd, (char*)&ofd, sizeof(ofd));
        printf("received fd: %d, extradata ofd : %d\n", fd,ofd);
        if( fd >-1)
        {
            printf("ah , ah standard output is testing... \n");
            dup2(fd,1);
            printf(" hello world!\n");
        }
    }

    return 0;
}

int recvFD(int sockfd, int* fd, char* buf, size_t buflen){
    struct msghdr msg;
    struct iovec iov;
    cmsg_fd cmsg;
    *fd=-1;
   
    iov.iov_base = buf;
    iov.iov_len = buflen;
   
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = &iov;
    msg.msg_iovlen =1;

    msg.msg_control = &cmsg;
    msg.msg_controllen = sizeof(cmsg);
   
    msg.msg_flags = 0;

    if( -1 ==recvmsg(sockfd, &msg, 0) ){
        printf("err:%d\n",getpid());
        return -1;   
    }

    const struct cmsghdr* cptr = CMSG_FIRSTHDR(&msg) ;
    if( !cptr)
    {
        printf("err:%d\n",getpid() );
        return 1;
    }

    if( SOL_SOCKET != cptr->cmsg_level || SCM_RIGHTS != cptr->cmsg_type )
    {
        printf("err:invalid control message %d\n",getpid() );
        return 1;
    }

    memcpy(fd, CMSG_DATA(cptr), sizeof(*fd));
    return 0;
}
int sendFD(int sockfd, int fd, const char* buf, size_t buflen){
    struct msghdr msg;
    struct iovec iov;
    cmsg_fd cmsg;
   
    iov.iov_base = (void*)buf;
    iov.iov_len = buflen;
   
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
   
    msg.msg_control = &cmsg;
    msg.msg_controllen = sizeof(cmsg);
   
    msg.msg_flags = 0;
   
    struct cmsghdr* cptr = CMSG_FIRSTHDR(&msg);
   
    cptr->cmsg_len = CMSG_LEN(sizeof(int));
    cptr->cmsg_level = SOL_SOCKET;
    cptr->cmsg_type = SCM_RIGHTS;
   
    memcpy(CMSG_DATA(cptr), &fd, sizeof(int));
   
    if ( -1 == sendmsg( sockfd, &msg, 0))
    {
        printf("err: sendmsg func %d\n",getpid());
        return 1;
    }
    return 0;
}

'네트워크 > network' 카테고리의 다른 글

IPv6  (0) 2011.04.15
네트워크 용어  (0) 2011.04.15
SET( Secure Electronic Transaction )  (0) 2011.04.12
SSL( Secure Socket Layer )  (0) 2011.04.12
RPC(Remote Procedure Call)  (0) 2011.03.12

+ Recent posts