2011-05-06 1 views
3

Semble que le flux ne va pas dans la boucle "for" contenant accepter dans ipv6server.c et donc pas en mesure d'accepter et de se connecter avec le client. Quelle est l'erreur? Ce code fonctionne bien pour IPV4 mais après IPV6 change obtenir ce problèmeProblème de programme de socket IPv6

ipv6server.c 

    #include <stdio.h> 
    #include <stdlib.h> /* needed for os x */ 
    #include <string.h> /* for memset */ 
    #include <sys/socket.h> 
    #include <netinet/in.h> 
    #include <sys/errno.h> /* defines ERESTART, EINTR */ 
    #include <sys/wait.h> /* defines WNOHANG, for wait() */ 

    #include "port.h"  /* defines default port */ 

    #ifndef ERESTART 
    #define ERESTART EINTR 
    #endif 

extern int errno; 

    void serve(int port); /* main server function */ 
    void disconn(void); 

    main(int argc, char **argv) 
    { 
     extern char *optarg; 
     extern int optind; 
     int c, err = 0; 
     int port = SERVICE_PORT; 
     static char usage[] = "usage: %s [-d] [-p port]\n"; 

    while ((c = getopt(argc, argv, "dp:")) != -1) 
     switch (c) { 
     case 'p': 
      port = atoi(optarg); 
      if (port < 1024 || port > 65535) { 
       fprintf(stderr, "invalid port number: %s\n", optarg); 
       err = 1; 
      } 
      break; 
     case '?': 
      err = 1; 
      break; 
     } 
    if (err || (optind < argc)) { 
     fprintf(stderr, usage, argv[0]); 
     exit(1); 
    } 
    serve(port); 
} 

/* serve: set up the service */ 

    void 
    serve(int port) 
    { 
     int svc;  /* listening socket providing service */ 
     int rqst;  /* socket accepting the request */ 
     socklen_t alen;  /* length of address structure */ 
     struct sockaddr_in6 my_addr; /* address of this service */ 
     struct sockaddr_in6 client_addr; /* client's address */ 
     int sockoptval = 1; 
     char hostname[128]; /* host name, for debugging */ 

    gethostname(hostname, 128); 

    /* get a tcp/ip socket */ 
    /* AF_INET is the Internet address (protocol) family */ 
    /* with SOCK_STREAM we ask for a sequenced, reliable, two-way */ 
    /* conenction based on byte streams. With IP, this means that */ 
    /* TCP will be used */ 

    if ((svc = socket(AF_INET6, SOCK_STREAM, 0)) < 0) { 
     perror("cannot create socket"); 
     exit(1); 
    } 

    /* we use setsockopt to set SO_REUSEADDR. This allows us */ 
    /* to reuse the port immediately as soon as the service exits. */ 
    /* Some operating systems will not allow immediate reuse */ 
    /* on the chance that some packets may still be en route */ 
    /* to the port. */ 

    setsockopt(svc, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int)); 

    /* set up our address */ 
    /* htons converts a short integer into the network representation */ 
    /* htonl converts a long integer into the network representation */ 
    /* INADDR_ANY is the special IP address 0.0.0.0 which binds the */ 
    /* transport endpoint to all IP addresses on the machine. */ 

    memset((char*)&my_addr, 0, sizeof(my_addr)); /* 0 out the structure */ 
    my_addr.sin6_family = AF_INET6; /* address family */ 
    my_addr.sin6_port = htons(port); 
    my_addr.sin6_addr = in6addr_any; 

     client_addr.sin6_family = AF_INET6; /* address family */ 
    client_addr.sin6_port = htons(port); 
    client_addr.sin6_addr = in6addr_any; 

    /* bind to the address to which the service will be offered */ 
    if (bind(svc, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) { 
     perror("bind failed"); 
     exit(1); 
    } 

    /* set up the socket for listening with a queue length of 5 */ 
    if (listen(svc, 5) < 0) { 
     perror("listen failed"); 
     exit(1); 
    } 

    printf("server started on %s, listening on port %d\n", hostname, port); 

    /* loop forever - wait for connection requests and perform the service */ 
    alen = sizeof(client_addr);  /* length of address */ 

    for (;;) { 
     while ((rqst = accept(svc, 
         (struct sockaddr *)&client_addr, &alen)) < 0) { 
      /* we may break out of accept if the system call */ 
      /* was interrupted. In this case, loop back and */ 
      /* try again */ 
      if ((errno != ECHILD) && (errno != ERESTART) && (errno != EINTR)) { 
       perror("accept failed"); 
       exit(1); 
      } 
     } 

     printf("received a connection from: %s port %d\n", 
      inet_ntoa(client_addr.sin6_addr), ntohs(client_addr.sin6_port)); 
      shutdown(rqst, 2); /* close the connection */ 
    } 
} 



    ipv6client.c 


    /* 
     echoc: a demo of TCP/IP sockets connect 

     usage: client [-h serverhost] [-p port] 
    */ 

    #include <stdio.h> 
    #include <stdlib.h> /* needed for os x*/ 
    #include <string.h> /* for strlen */ 
    #include <netdb.h>  /* for gethostbyname() */ 
    #include <sys/socket.h> 
    #include <netinet/in.h> 

    #include "port.h"  /* defines default port */ 

    int conn(char *host, int port); 
    void disconn(void); 

    main(int argc, char **argv) 
    { 
     extern char *optarg; 
     extern int optind; 
     int c, err = 0; 
     char *prompt = 0; 
     int port = SERVICE_PORT; /* default: whatever is in port.h */ 
     char *host = "localhost"; /* default: this host */ 
     static char usage[] = 
         "usage: %s [-d] [-h serverhost] [-p port]\n"; 

    while ((c = getopt(argc, argv, "dh:p:")) != -1) 
     switch (c) { 
     case 'h': /* hostname */ 
      host = optarg; 
      break; 
     case 'p': /* port number */ 
      port = atoi(optarg); 
      if (port < 1024 || port > 65535) { 
       fprintf(stderr, "invalid port number: %s\n", optarg); 
       err = 1; 
      } 
      break; 
     case '?': 
      err = 1; 
      break; 
     } 
    if (err || (optind < argc)) { /* error or extra arguments? */ 
     fprintf(stderr, usage, argv[0]); 
     exit(1); 
    } 

    printf("connecting to %s, port %d\n", host, port); 

    if (!conn(host, port)) /* connect */ 
     exit(1); /* something went wrong */ 

    disconn(); /* disconnect */ 
    return 0; 
} 

    int fd; /* fd is the file descriptor for the connected socket */ 

    /* conn: connect to the service running on host:port */ 
    /* return 0 on failure, non-zero on success */ 
    int 
    conn(char *host, int port) 
    { 
     struct hostent *hp; /* host information */ 
     unsigned int alen; /* address length when we get the port number */ 
     struct sockaddr_in6 myaddr; /* our address */ 
     struct sockaddr_in6 servaddr; /* server address */ 
    printf("conn(host=\"%s\", port=\"%d\")\n", host, port); 

    /* get a tcp/ip socket */ 
    /* We do this as we did it for the server */ 
    /* request the Internet address protocol */ 
    /* and a reliable 2-way byte stream */ 

    if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) { 
     perror("cannot create socket"); 
     return 0; 
    } 

    /* bind to an arbitrary return address */ 
    /* because this is the client side, we don't care about the */ 
    /* address since no application will connect here --- */ 
    /* INADDR_ANY is the IP address and 0 is the socket */ 
    /* htonl converts a long integer (e.g. address) to a network */ 
    /* representation (agreed-upon byte ordering */ 

    memset((char *)&myaddr, 0, sizeof(myaddr)); 
    myaddr.sin6_family = AF_INET6; 
    myaddr.sin6_addr = in6addr_any; 
    myaddr.sin6_port = htons(0); 

    if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { 
     perror("bind failed"); 
     return 0; 
    } 

    /* this part is for debugging only - get the port # that the operating */ 
    /* system allocated for us. */ 
     alen = sizeof(myaddr); 
     if (getsockname(fd, (struct sockaddr *)&myaddr, &alen) < 0) { 
       perror("getsockname failed"); 
       return 0; 
     } 
    printf("local port number = %d\n", ntohs(myaddr.sin6_port)); 

    /* fill in the server's address and data */ 
    /* htons() converts a short integer to a network representation */ 

    memset((char*)&servaddr, 0, sizeof(servaddr)); 
    servaddr.sin6_family = AF_INET6; 
    servaddr.sin6_port = htons(port); 

    /* look up the address of the server given its name */ 
    hp = gethostbyname(host); 
    if (!hp) { 
     fprintf(stderr, "could not obtain address of %s\n", host); 
     return 0; 
    } 

    /* put the host's address into the server address structure */ 
    memcpy((void *)&servaddr.sin6_addr, hp->h_addr_list[0], hp->h_length); 

    /* connect to server */ 
    if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { 
     perror("connect failed"); 
     return 0; 
    } 
    return 1; 
} 

    /* disconnect from the service */ 
    void 
    disconn(void) 
    { 
     printf("disconn()\n"); 
     shutdown(fd, 2); /* 2 means future sends & receives are disallowed */ 
    } 
+0

Vous ne devriez pas avoir besoin de lier le client pour les flux TCP. Avez-vous vérifié chez le client quand il se connecte réellement qu'il a des informations d'adresse valides? – Boofhead

+0

Votre question est beaucoup trop longue. S'il vous plaît essayez de garder votre question à quelques lignes de code. –

+0

http://beej.us/guide/bgnet/output/html/multipage/inet_ntoaman.html indique que inet_ntoa est obsolète car il ne fonctionne que pour IPv4 - vous pouvez utiliser inet_ntop à la place. –

Répondre

5

hp = gethostbyname(host);

Comment savez-vous cela renvoie une adresse IPv6 si vous passez « localhost »? Il renvoie probablement l'adresse IPv4, et les choses se gâtent si vous essayez de le copier dans servaddr.sin6_addr

Utilisez getaddrinfo() et recherchez explicitement une adresse AF_INET6 (ou mieux encore, faites votre programme indépendant des types d'adresses réels, voir here) ou utiliser le in6addr_loopback global comme adresse de serveur, pour tester avec localhost.

4

je peux voir quelques questions à des degrés divers:

  1. Ne pas déclarer errno vous - utilisez les en-têtes. Il peut être une macro au lieu d'un int
  2. (par @Boofhead) ne pas bind la prise client
  3. Utilisation getaddrinfo() au lieu de gethostbyname() dans le client pour obtenir l'adresse du serveur. gethostbyname() ne supporte pas portativement IPv6

Le dernier des 3 est en fait le vrai problème. J'ai testé votre code sur MacOS X et CentOS 5 et gethostbyname() renvoie uniquement une adresse IPv4.