你需要知道的:
注意:首先你要有一个测试文件存在!( 因为下面要打开一个文件,然后传递它的描述符 )
1 >:
此程序的整体结构是:
进程1中创建一个socketpair用于父子进程通信
|
进程1中创建一个子进程
|
子进程中使用execl启动另一个进程来打开此文件并将fd通过参数传入的socket传回子进程
注意:父进程在这个时候的动作是waitpid而已
|
那么由于父子进程是的socket端口是全双工的,所以一旦子进程那里有信息就会发送过来
|
那么父进程就可以在一个端口上获得信息,然后再在自己的环境下打开文件,写到stdout
请注意:其中最重要的就是怎么使用struct msghdr来封装数据传递!!!
2 >:
关于msghdr结构体:
struct msghdr
{
void *msg_name; //!> 对方主机的地址
socklen_t msg_namelen; //!> 长度
struct iovec *msg_iov; //!> io vector 的内容
size_t msg_iovlen; //!> 个数
void *msg_control; //!> 控制信息
size_t msg_controllen; //!> 控制信息长度
int msg_flags; //!> 标志
};
分成4组,具体的信息网上到处都是,此处不多说哦~
3 >:
关于:struct cmsghdr
>: 我们在接收msghdr的时候需要这样一个结构体的指针来遍历整个CMSG_DATA的内容!
>: 在发送的时候需要使用这个写入头信息:pCmsghdr = CMSG_FIRSTHD(&msghdr_send);
pCmsghdr = CMSG_FIRSTHDR( &msghdr_send ); //!> the info of head
pCmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); //!> the msg len
pCmsghdr->cmsg_level = SOL_SOCKET; //!> -> stream mode
pCmsghdr->cmsg_type = SCM_RIGHTS; //!> -> file descriptor
*((int *)CMSG_DATA( pCmsghdr )) = file_fd; //!> data: the file fd
>: cmsg_len与CMSG_LEN()宏值所显示的长度相同。
cmsg_level 这个值表明了原始的协议级别(例如,SOL_SOCKET)。
cmsg_type 这个值表明了控制信息类型(例如,SCM_RIGHTS代表文件描述符)。
SCM_RIGHTS 附属数据对象是一个文件描述符
SCM_CREDENTIALS 附属数据对象是一个包含证书信息的结构
4 >:
关于一些宏:
>: CMSG_LEN:计算cmsghdr头结构加上所需要的填充字符的字节长度。
这个值用来设置cmsghdr对象的cmsg_len成员
上面的意思也就是说:输入的CMSG_DATA是什么类型的,对应的长度!
例如此处是fd,那么 就是CSMG_LEN(sizeof( int ))!!!
>: CMSG_SPACE:计算附属数据以及其头部所需的总空白。尽管CMSG_LEN()宏计算了一
个相似的长度,CMSG_LEN()值并不包括可能的结尾的填充字符。
CMSG_SPACE(sizeof fd)
此宏调用来得到所需的总空间
>: CMSG_DATA:接受一个指向cmsghdr结构的指针。返回的指针值指向跟随在头部以及填
充字节之后的附属数据的第一个字节(如果存在)。
fd = *(int *)CMSG_DATA(ptr);
>: CMSG_FIRSTHDR:返回一个指向附属数据缓冲区内的第一个附属对象的struct cmsghdr指
针。输入值为是指向struct msghdr结构的指针(不要与structcmsghdr相
混淆)。
这个宏会估计msghdr的成员msg_control与msg_controllen来确定在缓
冲区中是否存在附属对象。然后,他会计算返回的指针。如果不存在
附属数据对象则返回的指针值为NULL。否则,这个指针会指向存在
的第一个struct cmsghdr。这个宏用在一个for循环的开始处,来开始
在附属数据对象中遍历。
>: CMSG_NXTHDR:返回下一个附属数据对象的struct cmsghdr指针。这个宏会接受两个输
入参数:指向struct msghdr结构的指针指向当前struct cmsghdr的指针
如果没有下一个附属数据对象,这个宏就会返回NULL。遍历附属数据
当接收到一个附属数据时,我们可以使用CMSG_FIRSTHDR()与
CMSG_NXTHDR()宏来在附属数据对象中进行遍历。
5 >:
注意的是:此处是采用socketpair来创建两个socket来处理的,本是是采用的“全双工”模式而已~
6 >:
注意: WIFEXITED, WEXITSTATUS
WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。
( 当然status是由waitpid得到的!waitpid( pid, &status, 0 ) )
WEXITSTATUS:
解释:
WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),
WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。
第一个进程代码:
/*
这个进程需要做的就是创建一个子进程,然后
在子进程中尝试打开一个文件,注意是调用另
外一个进程处理,然后另外的进程将文件描述符
返回给此进程,再有此进程的处理!
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define BUFLINE 1024
//!> when we recv from the socket,we should give the same data struct
int recv_file_fd( int fd, void * data, size_t len, int * recv_fd )
{
struct msghdr msghdr_recv; //!> the info struct
struct iovec iov[1]; //!> io vector
size_t n; //!>
union
{
struct cmsghdr cm; //!> control msg
char ctl[CMSG_SPACE(sizeof( int ))]; //!> the pointer of char
}ctl_un;
struct cmsghdr * pCmsghdr = NULL; //!> the pointer of control
msghdr_recv.msg_control = ctl_un.ctl;
msghdr_recv.msg_controllen = sizeof( ctl_un.ctl );
//!> these infos are nosignification
msghdr_recv.msg_name = NULL; //!> the name
msghdr_recv.msg_namelen = 0; //!> len of name
iov[0].iov_base = data; //!> no data here
iov[0].iov_len = len; //!> the len of data
msghdr_recv.msg_iov = iov; //!> the io/vector info
msghdr_recv.msg_iovlen = 1; //!> the num of iov
if( ( n = recvmsg( fd, &msghdr_recv, 0 ) ) < 0 )//!> recv msg
{ //!> the msg is recv by msghdr_recv
printf("recv error : %d\n", errno);
exit(EXIT_FAILURE);
}
//!> now, we not use 'for' just because only one test_data_
if( ( pCmsghdr = CMSG_FIRSTHDR( &msghdr_recv ) ) != NULL //!> now we need only one,
&& pCmsghdr->cmsg_len == CMSG_LEN( sizeof( int ) ) //!> we should use 'for' when
) //!> there are many fds
{
if( pCmsghdr->cmsg_level != SOL_SOCKET )
{
printf("Ctl level should be SOL_SOCKET\n");
exit(EXIT_FAILURE);
}
if( pCmsghdr->cmsg_type != SCM_RIGHTS )
{
printf("Ctl type should be SCM_RIGHTS\n");
exit(EXIT_FAILURE);
}
*recv_fd =*((int*)CMSG_DATA(pCmsghdr)); //!> get the data : the file des*
}
else
{
*recv_fd = -1;
}
return n;
}
int open_file( char * path_name, int mode )
{
int fd;
int sockfd[2]; //!> for sockpair()
int status; //!> the status of child process
char c;
char arg_fd[10]; //> fd arg
char arg_mode[10]; //> mode arg
pid_t chi_pid;
//!> create the socketpair
if( socketpair( AF_LOCAL, SOCK_STREAM, 0, sockfd ) == -1 )
{
printf( "create socketpair error : %d\n", errno );
exit( EXIT_FAILURE );
}
//!> the child process
if( ( chi_pid = fork() ) == 0 )
{
close( sockfd[0] ); //!> child use the #1 port; so , close the #0
snprintf( arg_fd, sizeof( arg_fd ), "%d", sockfd[1] );
snprintf( arg_mode, sizeof( arg_mode ), "%d", mode );
//!> call the new process
execl( "./second", "second", arg_fd, path_name, arg_mode, (char*)NULL );
printf("execl the second process error : %d\n", errno);
//!> you know : the usge of execl
exit( EXIT_FAILURE );
}
//!> parent :
close( sockfd[1] ); //!> yes
waitpid( chi_pid, &status, 0 ); //!> wait the child process
if( WIFEXITED( status ) != 0 ) //!> we should know the usge of WIFEXITED
{
if( ( status = WEXITSTATUS( status ) ) == 0 ) //!> end
{
recv_file_fd( sockfd[0], &c, 1, &fd ); //!> recv from sockfd[0]
}
else
{
errno = status;
fd = -1;
}
close( sockfd[0] );
return fd;
}
return -1;
}
int main( int argc, char ** argv )
{
int fd; //!> get the file_descriptor
int n_read;
char buf[BUFLINE];
if( argc != 2 ) //!> input the file path
{
printf("Input : file path\n");
exit( EXIT_FAILURE );
}
if( ( fd = open_file( argv[1], O_RDONLY ) ) < 0 ) //!> fork a child
{ //!> then: child -> execl a new process
printf("Open file error : %d\n", errno); //!> parent -> wair and recv the fd from child
exit( EXIT_FAILURE );
}
printf("Get the new fd == %d\n", fd);
while( ( n_read = read( fd, buf, BUFLINE ) ) > 0 ) //!> read from fd
{
write( 1, buf, n_read ); //!> write to std_out
}
return 0;
}
第二个个进程代码:
/*
在这个文件中我们需要做的是:将打开的文件描述符,
这个文件描述符是第二个进程进行fork的子进程中init的,
所以也就是相当于在第二个进程的子进程中执行此程序,
此程序传递打开的文件描述符再给第二个进程!
这里其实是复杂化了程序,就是故意构造这样的一个状态!
呵呵!~
( 注意不仅仅是文件描述符,可以是所有的描述符 )
例如open()、pipe()、mkfifo()、socket()或者accept() 都可以
注意:此进程的是由第二个进程进行调用的!
use UDP
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
int send_back( int fd_send_to, void * data, size_t len, int file_fd )
{
struct msghdr msghdr_send; //!> the info struct
struct iovec iov[1]; //!> io vector
size_t n; //!>
union
{
struct cmsghdr cm; //!> control msg
char ctl[CMSG_SPACE(sizeof( int ))]; //!> the pointer of char
}ctl_un;
struct cmsghdr * pCmsghdr = NULL; //!> the pointer of control
msghdr_send.msg_control = ctl_un.ctl;
msghdr_send.msg_controllen = sizeof( ctl_un.ctl );
//!> design : the first info
pCmsghdr = CMSG_FIRSTHDR( &msghdr_send ); //!> the info of head
pCmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); //!> the msg len
pCmsghdr->cmsg_level = SOL_SOCKET; //!> -> stream mode
pCmsghdr->cmsg_type = SCM_RIGHTS; //!> -> file descriptor
*((int *)CMSG_DATA( pCmsghdr )) = file_fd; //!> data: the file fd
//!> these infos are nosignification
msghdr_send.msg_name = NULL; //!> the name
msghdr_send.msg_namelen = 0; //!> len of name
iov[0].iov_base = data; //!> no data here
iov[0].iov_len = len; //!> the len of data
msghdr_send.msg_iov = iov; //!> the io/vector info
msghdr_send.msg_iovlen = 1; //!> the num of iov
return ( sendmsg( fd_send_to, &msghdr_send, 0 ) ); //!> send msg now
}
int main( int argc, char ** argv )
{
int fd; //!> the fd of the open_file
ssize_t ret_n; //!> the return of n
if( argc != 4 )
{
printf("useg:<unix_domain socket> <file_name> <open_mode>\n");
exit( 0 );
}
//!> open the file
if( ( fd = open( argv[2], atoi( argv[3] ) ) ) < 0 )
{
printf("open file error : %d\n", errno);
exit( EXIT_FAILURE );
}
printf("Open the file, the fd == %d\n", fd);
if( ( ret_n = send_back( atoi( argv[1]) , "", 1, fd ) ) < 0 ) //!> send back by the socket
{
printf("send back error : %d\n", errno);
exit( EXIT_FAILURE );
}
return 0;
}
在我的机子上输出的结果:
pt@ubuntu:~/桌面$ ./f file
Open the file, the fd == 3
Get the new fd == 4
//!> 注意,下面是我的文件中的内容
sdfvas
dv
asdf
sdfb
sfgn
d
sfynhfdgobfnadlfknjvbasdf
bsdf'bsmd
gfbka
dfk
ag
as
s