원본글 주소 : 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)하면 데이터부분만 포인터로 접근할 수 있다.
자, 이제 예제 소스이다. _-_
[출처] 소켓을 통해 다른 프로세스에 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 |