在一般的socket实现的时候,通常是用参数的形式,将文件描述符(FD)传到子进程或者直接传到read()汉书中。
不过如果想以Socket的形式,来传送FD的话,那就要用到sendmsg和recvmsg函数了。关键点,FD的值要通过msg.msg_control来传递的,千万别写到传输用的buff里面,那样做只是简单的传值,没有任何意义的。
/*
-------------------------------------------------
server
-------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#define PORT1 (u_short)11005
#define PORT2 (u_short)11002
/*
-------------------------------------------------
グローバル変数
-------------------------------------------------
*/
struct hostent *myhost;
char hostname[257];
char buff[257];
struct sockaddr_in me;
struct sockaddr_in aite;
int sockfd;
int fd = 0;
int s_retry_counter;
int ret;
int status;
int pid;
int aiteaddrlen;
fd_set l_fds;
int sendmsg_fd;
int recvmsg_fd;
int rcv_proc();
/*-------------------------------------------------
メインルーチン
-------------------------------------------------
*/
int main () {
int l_rtn;
struct cmsghdr *l_cmsg;
struct msghdr msg;
struct iovec iov[1];
int send_buf = 1;
int l_length;
char cms_data[sizeof(struct cmsghdr) + sizeof(int)];
int l_fd_pair[2];
if(socketpair(PF_UNIX, SOCK_STREAM, 0, l_fd_pair) != 0)
{
fprintf(stderr,"Create socketpair failed.\n");
}
pid = fork();
switch(pid)
{
case -1: //エラー
// 親側のソケットをクローズする
fprintf(stderr,"fork() failled.\n");
break;
case 0: //子プロセス側
close(l_fd_pair[0]);
recvmsg_fd = l_fd_pair[1];
rcv_proc();
break;
default: //親プロセス側
close(l_fd_pair[1]);
sendmsg_fd = l_fd_pair[0];
break;
}
/*ソケットを作成*/
if ((sockfd = socket(AF_INET, SOCK_STREAM , 0)) < 0 ) {
fprintf(stderr,"Socket for server failed.\n");
return -1;
}
/*自ホスト名の取得*/
bzero(hostname,257);
gethostname(hostname,256);
/*自IPアドレスを取得*/
if((myhost = gethostbyname(hostname)) == NULL) {
fprintf(stderr,"bad hostname!\n");
return -1;
}
/*自ホスト情報を構造体にセット*/
bzero((char *)&me,sizeof(me));
me.sin_family = AF_INET;
me.sin_port = htons(PORT1);
bcopy(myhost->h_addr, (char *)&me.sin_addr, myhost->h_length);
/*BIND*/
l_rtn = bind ( sockfd, (struct sockaddr *)&me, sizeof(me));
if( l_rtn == -1)
{
fprintf(stderr, "Server could not bind.\n");
return -1;
}
printf("Successfully bound in PORT1 %u.\n",PORT1);
/*listen*/
if ( (listen ( sockfd , 5)) == -1) {
fprintf(stderr, "Listen failed.\n");
return -1;
}
/*
-------------------------------------------------
ACCEPT
-------------------------------------------------
*/
s_retry_counter=0;
fd = 0;
while(1) {
/* FD_ZERO( &l_fds );
FD_SET(sockfd, &l_fds );
l_rtn = select( sockfd+1, &l_fds, NULL, NULL, NULL);
if(l_rtn == -1)
{
fprintf(stderr,"Select error !!\n");
return -1;
}
if( FD_ISSET(sockfd, &l_fds) )
{
*/
/* acceptを試みる */
aiteaddrlen = sizeof(aite);
fd = accept( sockfd, ( struct sockaddr * )&aite , &aiteaddrlen);
/* accept失敗 */
if (fd == -1) {
fprintf(stderr,"Accept failed.\n");
return -1;
}
printf("accept fd : %d\n", fd);
// send_buf = fd;
l_length = sizeof(struct cmsghdr) + sizeof(int);
l_cmsg = (struct cmsghdr*)cms_data;
l_cmsg->cmsg_level = SOL_SOCKET;
l_cmsg->cmsg_type = SCM_RIGHTS;
l_cmsg->cmsg_len = l_length;
bzero(&msg, sizeof(msg));
msg.msg_name = NULL; /* attention this is a pointer to void* type */
msg.msg_namelen = 0;
iov[0].iov_base = &send_buf;
iov[0].iov_len = sizeof(send_buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = (caddr_t)l_cmsg;
msg.msg_controllen = l_length;
*(int *)CMSG_DATA(l_cmsg) = fd;
printf("sendmsg begin. sendmsg_fd = %d\n", sendmsg_fd);
l_rtn = sendmsg( sendmsg_fd, &msg, 0 );
if(l_rtn == -1 )
{
fprintf(stderr,"Sendmsg failed. errno : %s\n",strerror(errno));
return -1;
}
printf("Sendmsg Success!\n");
// }
}
return 0;
}
/*
=================================================
子プロセス (s ソケット)
正常終了:0 異常終了:-1
=================================================
*/
int rcv_proc()
{
int l_rtn;
int cnt;
int buff_size;
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr *cmsg_hdr;
int cmsg_len;
int recv_buf;
int rcv_fd = 0;
fd_set l_fds;
// fd_set readok;
int width;
char recv_data[sizeof(struct cmsghdr) + sizeof(int)];
cmsg_hdr = (struct cmsghdr*)recv_data;
cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
printf("rcr_proc begin.\n");
for(;;)
{ bzero(&msg, sizeof(msg));
msg.msg_name = NULL; /* attention this is a pointer to void* type */
msg.msg_namelen = 0;
iov[0].iov_base = &recv_buf;
iov[0].iov_len = sizeof(recv_buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = (caddr_t)cmsg_hdr;
msg.msg_controllen = cmsg_len;
printf("recvmsg begin. recvmsg_fd = %d\n", recvmsg_fd);
l_rtn = recvmsg( recvmsg_fd, &msg, 0);
if(l_rtn == -1 )
{
fprintf(stderr,"Recvmsg failed. errno : %s\n", strerror(errno));
return -1;
}
printf("Recvmsg Success!\n");
rcv_fd = *((int *)CMSG_DATA(cmsg_hdr));
FD_ZERO(&l_fds);
FD_SET(rcv_fd, &l_fds);
width = rcv_fd + 1;
/*
readok = l_fds;
l_rtn = select(width, &readok, NULL, NULL, NULL);
if(l_rtn == -1){
fprintf(stderr,"Selete failed. errno : %s\n", strerror(errno));
//return -1;
}
if( FD_ISSET(rcv_fd, (fd_set *)&readok) )
{
*/
printf("read fd : %d\n", rcv_fd);
printf("read() begin.\n");
bzero(buff,257);
buff_size = read ( rcv_fd, buff, 256 );
printf("buff_size : %d\n", buff_size);
if(buff_size == -1){
fprintf(stderr, "Read failed. errno : %s\n", strerror(errno));
return -1;
}
printf("Received buffer : ");
for (cnt=0; cnt< buff_size; cnt++) {
putchar(buff[cnt]);
}
// }
}
return 1;
}
/*
-------------------------------------------------
client
-------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#define PORT (u_short)11005
/*
-------------------------------------------------
グローバル変数
-------------------------------------------------
*/
char hostname[257];
char buff[257];
struct hostent *connect_host;
struct sockaddr_in desthost;
int s_listen;
int s;
int s_retry_counter;
int ret;
int status;
int pid;
int portno;
/*
-------------------------------------------------
メインルーチン
-------------------------------------------------
*/
int main () {
/*接続先ホスト名設定*/
bzero(hostname,257);
strcpy(hostname,"127.0.0.1");
/*接続先IPアドレス取得*/
if((connect_host = gethostbyname(hostname)) == NULL) {
fprintf(stderr,"bad hostname!\n");
return -1;
}
/*接続先情報をソケット型構造体にセット*/
bzero((char *)&desthost,sizeof(desthost));
desthost.sin_family = AF_INET;
desthost.sin_port = htons(PORT);
bcopy(connect_host->h_addr, (char *)&desthost.sin_addr, connect_host->h_length);
/*ソケットを作成*/
if ((s_listen = socket(AF_INET, SOCK_STREAM,0)) < 0 ) {
fprintf(stderr,"Socket for client failed.\n");
return -1;
}
/*コネクト*/
if (connect(s_listen, ( struct sockaddr * )&desthost , sizeof(desthost) ) == -1) {
fprintf(stderr,"Connect failed.\n");
return -1;
}
/*文字列送信*/
bzero(buff,257);
strcpy(buff,"Test ABC.\n");
if ( write(s_listen, buff, 256 ) == -1 ) {
fprintf(stderr,"Write failed.\n");
return -1;
}
/*ソケット切断*/
close(s_listen);
/*終了*/
return 0;
}