在C中连接IPv6的问题

时间:2016-01-17 11:49:26

标签: c sockets ipv6 berkeley-sockets

我试图编写一个不可知的回显服务器,它可以接受IPv4和IPv6连接。我正在使用addrinfo结构,使用getaddrinfo进行设置 Ipv4连接没有问题,而我无法获得正常工作的ipV6连接。 我认为我的问题可能是由于错误的getaddrinfo参数,但我无法看到我出错的地方。
这是我的代码

client.c

#include <stdio.h>      
#include <sys/types.h>
#include <sys/socket.h>   
#include <netdb.h>
#include <stdlib.h>
#include <string.h> 
#include <errno.h>

int main(int argc, char *argv[])
{
      int simpleSocket = 0, simplePort = 0,returnStatus = 0, n; 
      char buffer[1024] = "";
      struct hostent *hostinfo;
      struct addrinfo simpleServer, *res;

      if (3 != argc) {
          fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
          exit(1);
      }

      simplePort = atoi(argv[2]);    

      memset(&simpleServer, 0, sizeof simpleServer);
      simpleServer.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
      simpleServer.ai_socktype = SOCK_STREAM;
      simpleServer.ai_flags = AI_PASSIVE;     // fill in my IP for me

      returnStatus = getaddrinfo(argv[1], argv[2], &simpleServer, &res);

      simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

      char *s = NULL;
      switch(res->ai_addr->sa_family) {
      case AF_INET: {
            struct sockaddr_in *addr_in = (struct sockaddr_in *)res;
            s = malloc(INET_ADDRSTRLEN);
            inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
            returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
            break;
      }
      case AF_INET6: {
            struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res;
            s = malloc(INET6_ADDRSTRLEN);
            inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
            returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
            break;
      }
      default:
            break;
      }
      fprintf(stdout, "IP address: %s\n", s);

      returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
      fprintf(stdout, "Type a message \n");

      memset(buffer, '\0', strlen(buffer));
      fgets(buffer, sizeof(buffer), stdin);
      returnStatus = write(simpleSocket, buffer, sizeof(buffer));

      memset(&buffer, '\0', sizeof(buffer));
      fprintf(stdout, "Waiting server..\n");     
      returnStatus = read(simpleSocket, buffer, sizeof(buffer));

      fprintf(stdout, "Message: %s\n", buffer);

      close(simpleSocket);
      return 0;
}

server.c

#include <stdio.h>      
#include <sys/types.h>
#include <sys/socket.h>   
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[])
{
      int simpleSocket = 0, simplePort = 0, returnStatus = 0, check = 1, n; 
      char buffer[1024];

      struct addrinfo simpleServer, *res;

      if (2 != argc) {
          fprintf(stderr, "Usage: %s <port>\n", argv[0]);
          exit(1);
      }

      simplePort = atoi(argv[1]);

      memset(&simpleServer, 0, sizeof simpleServer);
      simpleServer.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
      simpleServer.ai_socktype = SOCK_STREAM;
      simpleServer.ai_flags = AI_PASSIVE;     // fill in my IP for me

      getaddrinfo(NULL, argv[1], &simpleServer, &res);

      simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
      returnStatus =bind(simpleSocket, res->ai_addr, res->ai_addrlen);
      returnStatus = listen(simpleSocket, 5);

      struct addrinfo clientName = { 0 };
      int clientNameLength = sizeof(clientName);
      int simpleChildSocket = 0;

      while (1) {
         while (1) { 
            simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength);

            fprintf(stdout,"Waiting..\n");

            memset(&buffer, '\0', sizeof(buffer));        
            returnStatus = read(simpleChildSocket, buffer, sizeof(buffer));

            fprintf(stdout, "Message: %s\n", buffer);

            write(simpleChildSocket, buffer, sizeof(buffer));     
         }
      }
      close(simpleChildSocket);
      close(simpleSocket);
      return 0;
}

2 个答案:

答案 0 :(得分:3)

正如@JoachimPileborg在评论中提到的,问题是你的服务器代码没有为getaddrinfo()给你的每个地址打开监听套接字。您在AF_UNSPEC结构中指定了simpleServer,因此getaddrinfo()将为您提供两个 IPv4和IPv6地址的列表。您正在为该列表中的第一个条目创建一个侦听套接字,该条目恰好是IPv4地址。这就是您的IPv4客户端成功并且您的IPv6客户端失败的原因。您需要遍历列表,为每个条目创建一个单独的侦听套接字。

您还犯了其他错误,例如在您每次拨打clientNameLength时都没有重置accept()变量,当您应该使用addrinfo作为clientName缓冲区时{ {1}}而不是注意sockaddr_storage的返回值。

尝试更像这样的事情:

read()

话虽如此,如果您在支持双栈套接字的平台上运行,您的服务器根本不必创建任何IPv4侦听套接字。它只能创建IPv6套接字,然后禁用其include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <poll.h> #define MAX_SERVERS 10 #define MAX_CLIENTS 50 #define MAX_SOCKETS (MAX_SERVERS + MAX_CLIENTS) int main(int argc, char *argv[]) { int simpleSocket, simplePort, returnStatus, n, m; char buffer[1024]; pollfd simpleSockets[MAX_SOCKETS]; int numSockets = 0, numServers = 0; struct addrinfo simpleServer, *res, *addr; if (2 != argc) { fprintf(stderr, "Usage: %s <port>\n", argv[0]); exit(1); } simplePort = atoi(argv[1]); memset(&simpleServer, 0, sizeof simpleServer); simpleServer.ai_family = AF_UNSPEC; // use IPv4 and/or IPv6, whatever is available simpleServer.ai_socktype = SOCK_STREAM; simpleServer.ai_flags = AI_PASSIVE; // fill in my IP for me if (0 != getaddrinfo(NULL, argv[1], &simpleServer, &res)) { fprintf(stderr, "getaddrinfo failed\n"); exit(1); } addr = res; while (NULL != addr) { simpleSocket = socket(res->ai_family, addr->ai_socktype, addr->ai_protocol); if (-1 == simpleSocket) { fprintf(stderr, "socket failed\n"); } else { returnStatus = bind(simpleSocket, addr->ai_addr, addr->ai_addrlen); if (0 == returnStatus) returnStatus = listen(simpleSocket, 5); if (0 == returnStatus) { simpleSockets[numSockets].fd = simpleSocket; simpleSockets[numSockets].events = POLLIN; simpleSockets[numSockets].revents = 0; ++numSockets; ++numServers; if (MAX_SERVERS == numServers) break; } else { fprintf(stderr, "bind/listen failed\n"); close(simpleSocket); } } addr = addr->next; } freeaddrinfo(res); if (0 == numServers) { fprintf(stderr, "no servers are listening\n"); exit(1); } struct sockaddr_storage clientName; int clientNameLength; while (1) { returnStatus = poll(simpleSockets, numSockets, -1); if (-1 == returnStatus) { fprintf(stderr, "poll failed\n"); exit(1); } if (0 == returnStatus) continue; for (n = 0; n < numSockets; ++n) { if (simpleSockets[n].revents & POLLIN) { if (n < numServers) { clientNameLength = sizeof(clientName); simpleSocket = accept(simpleSockets[n].fd, (struct sockaddr *)&clientName, &clientNameLength); if (-1 == simpleSocket) { fprintf(stderr, "accept failed\n"); continue; } for (m = numServers; m < numSockets; ++m) { if (-1 == simpleSockets[m].fd) { simpleSockets[m].fd = simpleSocket; simpleSockets[m].events = POLLIN; simpleSockets[m].revents = 0; simpleSocket = -1; break; } } if ((-1 != simpleSocket) && (MAX_SOCKETS > numSockets)) { simpleSockets[numSockets].fd = simpleSocket; simpleSockets[numSockets].events = POLLIN; simpleSockets[numSockets].revents = 0; ++numSockets; simpleSocket = -1; } if (-1 != simpleSocket) { fprintf(stderr, "Too many clients connected\n"); close(simpleSocket); } else fprintf(stdout, "Client connected\n"); } else { returnStatus = read(simpleSockets[n].fd, buffer, sizeof(buffer)); if (0 >= returnStatus) { if (0 > returnStatus) fprintf(stdout, "Client error, disconnecting\n"); else fprintf(stdout, "Client disconnected\n"); close(simpleSockets[n].fd); simpleSockets[n].fd = -1; simpleSockets[n].events = 0; simpleSockets[n].revents = 0; continue; } fprintf(stdout, "Message: %.*s\n", returnStatus, buffer); write(simpleSockets[n].fd, buffer, returnStatus); } } if (simpleSockets[n].revents & (POLLERR|POLLHUP|POLLNVAL)) { if (simpleSockets[n].revents & POLLHUP) fprintf(stdout, "Client disconnected\n"); else if (n >= numServers) fprintf(stdout, "Client error, disconnecting\n"); else fprintf(stdout, "Server error, closing\n"); close(simpleSockets[n].fd); simpleSockets[n].fd = -1; simpleSockets[n].events = 0; simpleSockets[n].revents = 0; } } } for (n = 0; n < numSockets; ++n) { if (-1 != simpleSockets[n].fd) close(simpleSockets[n].fd); } return 0; } 选项。这将允许他们接受IPv4和IPv6客户端。 IPV6_V6ONLY返回的客户端地址将告诉您IPv4或IPv6客户端是否已连接。

accept()

答案 1 :(得分:0)

client.c代码有缺陷,它将完整的addrinfo存储在sockaddr_in结构中。 res的类型为masterfile.txthttp://man7.org/linux/man-pages/man3/getaddrinfo.3.html

addrinfo

所以而不是
struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; }; 它应该是 struct sockaddr_in *addr_in = (struct sockaddr_in *)res;

喜欢

struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr;

此修改后,代码将按预期工作。