2013-06-13 6 views
5

Przeprowadzam testy z gniazdami domeny Unix i mogę komunikować się z nimi bez problemu, jednak gdy zadzwonię pod numer accept() po stronie serwera mojego programu testowego, zwrócony struct sockaddr_un nie zawiera sun_path.Unixowe gniazda domen: accept() nie ustawia ścieżki sun_path

Jestem prawie pewien, że gniazda Inet mają prawidłowo wypełniony adres i port po rozmowie accept(), więc czy robię coś nie tak w moim programie testowym lub czy oczekuję złego wyniku?

Używam CentOS 6.2 i gcc 4.4.6.

Kod

Próbka:

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

Wystarczy powtórzyć pytanie:

Dlaczego nie sun_path jest wypełniony po accept() zadzwoń na serwer?

+0

Naprawdę cud ... 'len' staje się' 2', który jest "sizeof (sa_family_t)" i według człowieka może się to zdarzyć tylko wtedy, gdy gniazdo jest niezwiązane, ale w twoim przypadku jest wyraźnie związane ... – kirelagin

+0

Próbowałem też z abstrakcyjnymi gniazdami i znowu ta długość zawsze jest "2", co jest oczywiście błędne, zgodnie ze stroną podręcznika. – kirelagin

+0

Myślę, że to może być normalne. Podobnie jak gniazda zwrócone przez 'socketpair()', tego typu gniazda nie są powiązane z żadną ścieżką, ale mimo to są podłączone do innego gniazda na drugim końcu. – Celada

Odpowiedz

4

Naprawdę nie jestem pewien, czy to w ogóle odpowiedź. Prawdopodobnie bardziej przypomina rozważania o niektórych badaniach, choć może warto je przeczytać.

Wartość wypełniona przez accept(2) wydaje się być dość niepoprawna dla protokołu przynajmniej w jądrze Linux 3.16.0, NetBSD 6.1.4 i Darwin 13.1.0. W praktyce oznacza to, że drugi parametr do accept(2), struct sockaddr * jest wypełniany tylko do tego, co jest wspólne dla wszystkich protokołów. Więc to, co masz w rękach po udanym acccept(2), wcale nie jest kompletnym struct sockaddr_un.

Prawdopodobnie nikt nie pomyślał, że będzie to miało duże znaczenie w momencie, gdy zostały wykonane pierwsze implementacje accept(2), a teraz utknęliśmy w tym. Na szczęście jest to na odwrót, na wypadek, gdyby ktoś utracił nazwę ścieżki używanej do połączenia z numerem bind(2) i chciałby ją teraz znaleźć.

Z struct sockaddr_storage i getsockname(2) ścieżka sun_path użytkownika jest dostępna. Tak więc, aby upewnić się, że otrzymujesz wszystkie soczyste szczegóły, zadzwoń getsockname(2) po pomyślnym wywołaniu accept(2) (to byłoby umieścić po linii numer 40 w 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); 
     } 

Lub po prostu użyć tego:

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

Zostało to przetestowane, tj. kompiluje i produkuje oczekiwane wyniki, pracuje nad systemem GNU/Linux z jądrem 3.16.0, systemem NetBSD z jądrem 6.1.4 i systemem wyposażonym w OS/X Mavericks, z jądrem 13.1.0. W tych wszystkich zachowaniach accept(2) jest spójny: sun_path nigdzie nie można znaleźć w wypełnionej strukturze. Zachowanie getsockname(2) jest również zgodne między różnymi środowiskami operacyjnymi, udostępniając wszystkie szczegółowe informacje dotyczące protokołu.

0

Nie powiązałeś gniazda klienta z adresem.

Nie trzeba wiązać gniazda klienta z adresem, aby funkcja connect() zadziałała; ale jeśli spodziewasz się uzyskać dostęp do swojego adresu klienta na serwerze, musisz wykonać bind().

Sens?

Wystarczy skonfigurować wywołanie funkcji bind() przed połączeniem się z klientem. Upewnij się, że ścieżka, której używasz dla swojego klienta, jest ważna i sprawdź błędy tak, jak zwykle.

Powiązane problemy