2011-05-06 41 views
3

似乎流不进入“for”循环包含在ipv6server.c中的接受,因此无法接受和连接到客户端。错误是什么?此代码工作正常进行IPV4但IPV6更改入门这个问题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

您不应该为TCP流绑定客户端。您是否在客户端验证其实际连接时是否具有有效的地址信息? – Boofhead 2011-05-06 06:24:24

+0

你的问题太长了。请尽量将您的问题保留在几行代码中。 – 2011-05-06 13:54:32

+0

http://beej.us/guide/bgnet/output/html/multipage/inet_ntoaman.html表示inet_ntoa已被弃用,因为它仅适用于IPv4 - 您可能需要使用inet_ntop。 – 2012-06-09 14:02:30

回答

5

hp = gethostbyname(host);

你怎么知道如果你传递“localhost”的这个返回的IPv6地址?它可能会返回IPv4地址,如果您尝试将其复制到servaddr.sin6_addr

使用getaddrinfo()并显式查找AF_INET6地址(或更好地让您的程序独立于实际的地址类型,请参阅here),或使用全局in6addr_loopback作为服务器地址,以使用localhost进行测试。

4

后,可看到不同程度的几个问题:

  1. 自己不要声明errno - 使用的标题。它可能是一个宏,而不是(每@Boofhead)的int
  2. 没有bind在客户端,客户端插座
  3. 使用getaddrinfo()而不是gethostbyname()获取服务器的地址。 gethostbyname()不可移植地支持IPv6

3中的最后一个实际上是真正的问题。我已经在MacOS X和CentOS 5上测试过你的代码,并且gethostbyname()只返回一个IPv4地址。