2013-06-13 18 views
5

Sto facendo alcuni test con i socket di dominio Unix e posso comunicare su di essi senza un problema, tuttavia, quando chiamo accept() sul lato server del mio programma di test, restituito struct sockaddr_un non contiene uno sun_path.Socket di dominio Unix: accept() non impostazione sun_path

Sono abbastanza sicuro che le prese Inet abbiano il loro indirizzo e la porta correttamente compilati dopo una chiamata accept(), quindi sto facendo qualcosa di sbagliato nel mio programma di test o mi aspetto un risultato sbagliato?

Sono in esecuzione CentOS 6.2 e gcc 4.4.6.

Codice

Esempio:

server.c

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

#define NAME "socket" 

int main(int argc, char **argv) 
{ 
    int sock, msgsock, rval; 
    struct sockaddr_un server, client; 
    char buf[1024]; 

    sock = socket(AF_UNIX, SOCK_STREAM, 0); 
    if (sock < 0) { 
     perror("opening stream socket"); 
     exit(1); 
    } 

    server.sun_family = AF_UNIX; 
    strcpy(server.sun_path, NAME); 

    if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) { 
     perror("binding stream socket"); 
     exit(1); 
    } 

    printf("Socket has name %s\n", server.sun_path); 
    listen(sock, 5); 

    for (;;) { 
     socklen_t len = sizeof(client); 
     msgsock = accept(sock, (struct sockaddr *)&client, &len); 

     if (msgsock == -1) 
      perror("accept"); 
     else do { 
      printf("strlen(sun_path) = %zu\n", strlen(client.sun_path)); 

      bzero(buf, sizeof(buf)); 
      if ((rval = read(msgsock, buf, 1024)) < 0) 
       perror("reading stream message"); 
      else if (rval == 0) 
       printf("Ending connection\n"); 
      else 
       printf("-->%s\n", buf); 
     } while (rval > 0); 

     close(msgsock); 
    } 
    close(sock); 
    unlink(NAME); 

    return 0; 
} 

client.c

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

#define DATA "Half a league, half a league . . ." 

int main(int argc, char **argv) 
{ 
    int sock; 
    struct sockaddr_un server; 

    if (argc < 2) { 
     printf("usage:%s <pathname>", argv[0]); 
     exit(1); 
    } 

    sock = socket(AF_UNIX, SOCK_STREAM, 0); 
    if (sock < 0) { 
     perror("opening stream socket"); 
     exit(1); 
    } 

    server.sun_family = AF_UNIX; 
    strcpy(server.sun_path, argv[1]); 

    if (connect(sock, (struct sockaddr *) &server, 
       sizeof(struct sockaddr_un)) < 0) { 
     close(sock); 
     perror("connecting stream socket"); 
     exit(1); 
    } 

    if (write(sock, DATA, sizeof(DATA)) < 0) 
     perror("writing on stream socket"); 

    close(sock); 

    return 0; 
} 

Giusto per ribadire la domanda:

Perché non sun_path viene compilato dopo la Chiamata accept() sul server?

+0

Davvero un miracolo ... 'len' diventa' 2' che è 'sizeof (sa_family_t)' e secondo l'uomo questo può accadere solo quando il socket non è associato, ma nel tuo caso è chiaramente vincolato ... – kirelagin

+0

Ho anche provato con prese astratte e, ancora, questa lunghezza è sempre '2' che è chiaramente sbagliata, che si accosta alla pagina man. – kirelagin

+0

Penso che questo potrebbe essere normale. Come i socket restituiti da 'socketpair()', questi tipi di socket non sono associati a nessun percorso, ma sono comunque connessi a un altro socket all'altro capo. – Celada

risposta

4

Non sono sicuro se questa sia una risposta. Probabilmente è più una riflessione su qualche ricerca, anche se forse vale ancora la pena di leggerlo.

Il valore inserito da accept(2) sembra essere abbastanza indipendente dal protocollo almeno in Linux 3.16.0, NetBSD 6.1.4 e kernel Darwin 13.1.0. In pratica ciò significa che il secondo parametro a accept(2), struct sockaddr * viene riempito solo fino a ciò che è condiviso tra tutti i protocolli. Quindi quello che hai tra le mani dopo il successo di acccept(2) è lungi dall'essere un completo struct sockaddr_un.

Probabilmente nessuno ha pensato che sarebbe stato di grande importanza al momento in cui sono state fatte le prime implementazioni di accept(2) e ora siamo bloccati con questo. Fortunatamente c'è un modo per aggirare questo, nel caso in cui uno abbia perso il percorso utilizzato per il socket con la chiamata a bind(2), e ora vorrebbe trovarlo di nuovo.

Con struct sockaddr_storage e getsockname(2) il membro sun_path è accessibile. Quindi, per assicurarsi che si stanno ottenendo tutti i dettagli succosi, chiamare getsockname(2) dopo una chiamata riuscita a accept(2) (questo sarebbe stato messo dopo riga numero 40 nella server.c):

 struct sockaddr_storage ss; 
     socklen_t sslen = sizeof(struct sockaddr_storage); 
     if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) { 
       struct sockaddr_un *un = (struct sockaddr_un *)&ss; 
       printf("socket name is: %s\n", un->sun_path); 
     } 

O semplicemente utilizzare questo:

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

#define NAME "socket" 

int main(int argc, char **argv) 
{ 
    int sock, msgsock, rval; 
    struct sockaddr_un server, client; 
    char buf[1024]; 

    sock = socket(AF_UNIX, SOCK_STREAM, 0); 
    if (sock < 0) { 
     perror("opening stream socket"); 
     exit(1); 
    } 

    server.sun_family = AF_UNIX; 
    strcpy(server.sun_path, NAME); 

    if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) { 
     perror("binding stream socket"); 
     exit(1); 
    } 

    printf("Socket has name %s\n", server.sun_path); 
    listen(sock, 5); 

    for (;;) { 
     socklen_t len = sizeof(client); 
     msgsock = accept(sock, (struct sockaddr *)&client, &len); 

     if (msgsock == -1) 
      perror("accept"); 
     else do { 
      printf("strlen(sun_path) = %zu\n", strlen(client.sun_path)); 

      struct sockaddr_storage ss; 
      socklen_t sslen = sizeof(struct sockaddr_storage); 
      if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) { 
        struct sockaddr_un *un = (struct sockaddr_un *)&ss; 
        printf("socket name is: %s\n", un->sun_path); 
      } 

      bzero(buf, sizeof(buf)); 
      if ((rval = read(msgsock, buf, 1024)) < 0) 
       perror("reading stream message"); 
      else if (rval == 0) 
       printf("Ending connection\n"); 
      else 
       printf("-->%s\n", buf); 
     } while (rval > 0); 

     close(msgsock); 
    } 
    close(sock); 
    unlink(NAME); 

    return 0; 
} 

Questo è stato testato, vale a dire. compila e produce risultati attesi, per lavorare su un sistema GNU/Linux con kernel 3.16.0, un sistema NetBSD con kernel 6.1.4 e un sistema equipaggiato con OS/X Mavericks, con il kernel 13.1.0. In tutti questi comportamenti di accept(2) è coerente: sun_path non si trova da nessuna parte nella struttura riempita. Il comportamento di getsockname(2) è coerente anche tra diversi ambienti operativi, rendendo disponibili tutti i dettagli specifici del protocollo.

0

Non si è associato il socket del client a un indirizzo.

Non è necessario collegare il socket del client a un indirizzo per connect() per funzionare; ma, se si prevede di accedere al proprio indirizzo client nel proprio server, è necessario eseguire il bind().

Ha senso?

Basta impostare una chiamata a bind() prima di connettersi al client. Assicurati che il percorso che utilizzi per il tuo cliente sia valido e controlli gli errori normalmente.