IPv6套接字程序问题

时间:2011-05-06 04:17:12

标签: c++ c sockets ipv6

似乎流不会进入ipv6server.c中包含accept的“for”循环,因此无法接受并连接客户端。怎么回事?此代码适用于IPV4,但在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 */
    }

2 个答案:

答案 0 :(得分:5)

我可以看到一些不同程度的问题:

  1. 不要自己声明errno - 使用标题。 可能是一个宏而不是int
  2. (根据@Boofhead)不要bind客户端套接字
  3. 在客户端中使用getaddrinfo()代替gethostbyname()来获取服务器的地址。 gethostbyname()无法支持IPv6
  4. 3的最后一个实际上是真正的问题。我已经在MacOS X和CentOS 5上测试了您的代码,而gethostbyname()只返回了一个IPv4地址。

答案 1 :(得分:5)

hp = gethostbyname(host);

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

,情况就会大打折扣

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