【问题标题】:IPv6 Socket Program ProblemIPv6 套接字程序问题
【发布时间】:2011-08-19 21:19:21
【问题描述】:

似乎流程不会进入包含 ipv6server.c 中的接受的“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 */
    }

【问题讨论】:

  • 您不需要为 TCP 流绑定客户端。您是否在客户端实际连接时验证它具有有效的地址信息?
  • 你的问题太长了。请尽量将您的问题保留在几行代码中。
  • beej.us/guide/bgnet/output/html/multipage/inet_ntoaman.html 说 inet_ntoa 已被弃用,因为它仅适用于 IPv4 - 您可能想改用 inet_ntop。
  • 注意:这个问题的代码 sn-p 在套接字上调用 shutdown(2),但从不调用 close(1)。我自己的代码中有错误,端口不可用于未来的套接字,因为我的代码基于上述问题的snipper,也没有在套接字上调用 close(1)。我认为关闭完成后应该关闭套接字。有关此主题的更多讨论,请参阅此问题:stackoverflow.com/questions/4160347/close-vs-shutdown-socket

标签: c++ c sockets ipv6


【解决方案1】:

hp = gethostbyname(host);

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

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

【讨论】:

    【解决方案2】:

    我可以看到几个不同程度的问题:

    1. 不要自己声明errno - 使用标题。它可能是一个宏而不是int
    2. (根据@Boofhead)不要bind 客户端套接字
    3. 在客户端使用getaddrinfo()而不是gethostbyname()来获取服务器地址。 gethostbyname() 不支持 IPv6

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-14
      • 1970-01-01
      • 2020-12-26
      • 1970-01-01
      相关资源
      最近更新 更多