2013-06-13 17 views
5

Ich mache ein paar Tests mit Unix-Domain-Sockets und ich kann über sie ohne ein Problem kommunizieren jedoch, wenn ich auf der Serverseite meines Testprogramms accept() aufrufen, die zurückgegebene struct sockaddr_un enthält keine sun_path.Unix Domain Sockets: accept() nicht set sun_path

Ich bin mir ziemlich sicher, dass die Inet-Buchsen ihre Adresse und ihren Port nach einem accept()-Aufruf korrekt ausgefüllt haben, also mache ich etwas falsch in meinem Testprogramm oder erwarte ich das falsche Ergebnis?

Ich benutze CentOS 6.2 und gcc 4.4.6.

Beispielcode:

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; 
} 

einfach die Frage zu wiederholen:

Warum sun_path nicht nach dem ausgefüllten accept() Anruf auf dem Server?

+0

Wahrlich ein Wunder ... 'len' wird' 2' das ist 'sizeof (sa_family_t)' und laut Mann kann das nur passieren, wenn der Socket ungebunden ist, aber in deinem Fall ist es eindeutig gebunden ... – kirelagin

+0

habe ich auch ausprobiert mit abstrakten Sockets und wiederum ist diese Länge immer '2' was eindeutig falsch ist, entsprechend der Manpage. – kirelagin

+0

Ich denke, das könnte normal sein. Wie Sockets, die von 'socketpair()' zurückgegeben werden, sind diese Socket-Typen nicht an einen Pfad gebunden, aber sie sind trotzdem mit einem anderen Socket am anderen Ende verbunden. – Celada

Antwort

4

Ich bin mir wirklich nicht sicher, ob das überhaupt eine Antwort ist. Wahrscheinlich ist es eher ein Nachdenken über etwas Forschung, obwohl es vielleicht noch lesenswert ist.

Der von accept(2) aufgefüllte Wert scheint zumindest in Linux 3.16.0, NetBSD 6.1.4 und Darwin 13.1.0 Kernel recht protokollunabhängig zu sein. In der Praxis bedeutet dies, dass der zweite Parameter zu accept(2), struct sockaddr * nur bis zu dem gefüllt wird, was zwischen allen Protokollen geteilt wird. Also was Sie in Ihren Händen nach einem erfolgreichen acccept(2) haben, ist weit davon entfernt, ein komplettes struct sockaddr_un zu sein.

Wahrscheinlich niemand dachte, es wäre von großer Bedeutung zu der Zeit erste Implementierungen von accept(2) waren getan und jetzt sind wir damit fest. Zum Glück gibt es einen Weg dahin, falls man den Pfadnamen für Socket mit Aufruf an bind(2) verloren hat und nun wieder finden möchte.

Mit struct sockaddr_storage und getsockname(2) ist der Member sun_path zugänglich. Also, um sicherzustellen, dass Sie alle pikanten Details bekommen, rufen Sie getsockname(2) nach einem erfolgreichen Aufruf accept(2) (dies würde nach Zeilennummer 40 in der server.c gestellt werden):

 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); 
     } 

Oder nur diese verwenden:

#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; 
} 

Dies wurde getestet, dh. Er kompiliert und produziert erwartete Ergebnisse, um auf einem GNU/Linux-System mit Kernel 3.16.0, einem NetBSD-System mit 6.1.4-Kernel und einem System mit OS/X-Mavericks mit 13.1.0-Kernel zu arbeiten. In allen diesen Verhaltens accept(2) ist konsistent: sun_path ist nirgends in der Struktur gefüllt zu finden. Das Verhalten von getsockname(2) ist auch zwischen verschiedenen Betriebsumgebungen konsistent und macht alle protokollspezifischen Details verfügbar.

0

Sie haben Ihren Client-Socket nicht an eine Adresse gebunden.

Sie müssen Ihren Client-Socket nicht an eine Adresse binden, damit connect() funktioniert; Wenn Sie jedoch auf Ihren Client auf Ihrem Server zugreifen möchten, müssen Sie bind().

Sinn machen?

Richten Sie einfach einen Aufruf von bind() ein, bevor Sie eine Verbindung in Ihrem Client herstellen. Stellen Sie sicher, dass der Pfad, den Sie für Ihren Client verwenden, gültig ist und prüfen Sie wie üblich auf Fehler.