2011-12-12 17 views
5

Dopo che @cnicutar mi ha risposto su questo question, ho provato a inviare un descrittore di file dal processo padre al figlio. Sulla base di questo example, ho scritto questo codice:Come utilizzare sendmsg() per inviare un descrittore di file tramite socket tra 2 processi?

int socket_fd ,accepted_socket_fd, on = 1; 
int server_sd, worker_sd, pair_sd[2]; 
struct sockaddr_in client_address; 
struct sockaddr_in server_address; 

/* ======================================================================= 
* Setup the network socket. 
* ======================================================================= 
*/ 

if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
{ 
    perror("socket()"); 
    exit(EXIT_FAILURE); 
} 

if((setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0) 
{ 
    perror("setsockopt()"); 
    exit(EXIT_FAILURE); 
} 

server_address.sin_family = AF_INET;     /* Internet address type */ 
server_address.sin_addr.s_addr = htonl(INADDR_ANY); /* Set for any local IP */ 
server_address.sin_port = htons(port);    /* Set to the specified port */ 

if(bind(socket_fd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) 
{ 
    perror("bind()"); 
    exit(EXIT_FAILURE); 
} 

if(listen(socket_fd, buffers) < 0) 
{ 
    perror("listen()"); 
    exit(EXIT_FAILURE); 
} 

if(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair_sd) < 0) 
{ 
    socketpair("bind()"); 
    exit(EXIT_FAILURE); 
} 

server_sd = pair_sd[0]; 
worker_sd = pair_sd[1]; 



/* ======================================================================= 
* Worker processes 
* ======================================================================= 
*/  

struct iovec iov[1]; 
struct msghdr child_msg; 
char msg_buffer[80]; 
int pass_sd, rc; 


/* Here the parent process create a pool of worker processes (its children) */ 
for(i = 0; i < processes; i++) 
{ 
    if(fork() == 0) 
    { 
     // ... 

     /* Loop forever, serving the incoming request */ 
     for(;;) 
     { 

      memset(&child_msg, 0, sizeof(child_msg)); 
      memset(iov, 0, sizeof(iov)); 

      iov[0].iov_base = msg_buffer; 
      iov[0].iov_len = sizeof(msg_buffer); 
      child_msg.msg_iov  = iov; 
      child_msg.msg_iovlen = 1; 
      child_msg.msg_name = (char *) &pass_sd; 
      child_msg.msg_namelen = sizeof(pass_sd); 

      printf("Waiting on recvmsg\n"); 
      rc = recvmsg(worker_sd, &child_msg, 0); 
      if (rc < 0) 
      { 
       perror("recvmsg() failed"); 
       close(worker_sd); 
       exit(-1); 
      } 
      else if (child_msg.msg_namelen <= 0) 
      { 
       printf("Descriptor was not received\n"); 
       close(worker_sd); 
       exit(-1); 
      } 
      else 
      { 
       printf("Received descriptor = %d\n", pass_sd); 
      } 

      //.. Here the child process can handle the passed file descriptor 
     } 
    } 

} 



/* ======================================================================= 
* The parent process 
* ======================================================================= 
*/ 

struct msghdr parent_msg; 
size_t length; 

/* Here the parent will accept the incoming requests and passed it to its children*/ 
for(;;) 
{ 
    length = sizeof(client_address); 
    if((accepted_socket_fd = accept(socket_fd, NULL, NULL)) < 0) 
    { 
     perror("accept()"); 
     exit(EXIT_FAILURE); 
    } 

    memset(&parent_msg, 0, sizeof(parent_msg)); 
    parent_msg.msg_name = (char *) &accepted_socket_fd; 
    parent_msg.msg_namelen = sizeof(accepted_socket_fd); 

    if((sendmsg(server_sd, &parent_msg, 0)) < 0) 
    { 
     perror("sendmsg()"); 
     exit(EXIT_FAILURE); 
    } 

} 

Ma, purtroppo, ho ottenuto questo errore:

sendmsg(): Invalid argument 

Cosa devo fare per risolvere questo problema? e sto usando la struttura msghdr correttamente? perché nell'esempio che ho menzionato sopra, usano msg_accrights e msg_accrightslen e ho ricevuto qualche errore quando li uso, quindi ho dovuto usare msg_name e msg_namelen.

risposta

8

Questo è estremamente difficile da ottenere. Consiglierei solo l'uso di una libreria che lo fa per te. Uno dei più semplici è libancillary. Ti dà due funzioni, una per inviare un descrittore di file su un socket di dominio UNIX e uno per riceverne uno. Sono assurdamente semplici da usare.

0

Non è possibile inviare descrittori di file su AF_INET. Utilizzare un socket di dominio UNIX.

+0

L'utente non sta utilizzando AF_INET per il passaggio del socket, ma un socket AF_UNIX creato da socketpair. – Nakedible

6

Il problema è che si passa il descrittore di file in un campo msg_name. Questo è un campo indirizzo, e non è destinato a passare dati arbitrari.

In effetti, i descrittori di file dovrebbero essere passati in modo speciale in modo che il kernel possa duplicare il descrittore di file per il processo di ricezione (e forse il descrittore avrà un altro valore dopo la duplicazione). Ecco perché esiste un tipo di messaggio accessorio speciale (SCM_RIGHTS) per passare i descrittori di file.

Quanto segue funzionerebbe (ho omesso parte della gestione degli errori). In cliente:

memset(&child_msg, 0, sizeof(child_msg)); 
char cmsgbuf[CMSG_SPACE(sizeof(int))]; 
child_msg.msg_control = cmsgbuf; // make place for the ancillary message to be received 
child_msg.msg_controllen = sizeof(cmsgbuf); 

printf("Waiting on recvmsg\n"); 
rc = recvmsg(worker_sd, &child_msg, 0); 
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&child_msg); 
if (cmsg == NULL || cmsg -> cmsg_type != SCM_RIGHTS) { 
    printf("The first control structure contains no file descriptor.\n"); 
    exit(0); 
} 
memcpy(&pass_sd, CMSG_DATA(cmsg), sizeof(pass_sd)); 
printf("Received descriptor = %d\n", pass_sd); 

Nel server:

memset(&parent_msg, 0, sizeof(parent_msg)); 
struct cmsghdr *cmsg; 
char cmsgbuf[CMSG_SPACE(sizeof(accepted_socket_fd))]; 
parent_msg.msg_control = cmsgbuf; 
parent_msg.msg_controllen = sizeof(cmsgbuf); // necessary for CMSG_FIRSTHDR to return the correct value 
cmsg = CMSG_FIRSTHDR(&parent_msg); 
cmsg->cmsg_level = SOL_SOCKET; 
cmsg->cmsg_type = SCM_RIGHTS; 
cmsg->cmsg_len = CMSG_LEN(sizeof(accepted_socket_fd)); 
memcpy(CMSG_DATA(cmsg), &accepted_socket_fd, sizeof(accepted_socket_fd)); 
parent_msg.msg_controllen = cmsg->cmsg_len; // total size of all control blocks 

if((sendmsg(server_sd, &parent_msg, 0)) < 0) 
{ 
    perror("sendmsg()"); 
    exit(EXIT_FAILURE); 
} 

Vedi anche man 3 cmsg, ci sono alcuni esempi.

+1

Dopo sendmsg, qual è lo stato di fd nel server? È aperto e deve essere chiuso oppure sendmsg/cmsg lo chiude? Nel codice di esempio non vedo mai il server che chiude il file fd, che sarebbe necessario se sendmsg non lo chiudesse. –

Problemi correlati