在 C 语言中 IPv6 连接的问题



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

客户端.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: %sn", s);
      returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
      fprintf(stdout, "Type a message n");
      memset(buffer, '', strlen(buffer));
      fgets(buffer, sizeof(buffer), stdin);
      returnStatus = write(simpleSocket, buffer, sizeof(buffer));
      memset(&buffer, '', sizeof(buffer));
      fprintf(stdout, "Waiting server..n");     
      returnStatus = read(simpleSocket, buffer, sizeof(buffer));
      fprintf(stdout, "Message: %sn", buffer);
      close(simpleSocket);
      return 0;
}

服务器.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, '', sizeof(buffer));        
            returnStatus = read(simpleChildSocket, buffer, sizeof(buffer));
            fprintf(stdout, "Message: %sn", buffer);
            write(simpleChildSocket, buffer, sizeof(buffer));     
         }
      }
      close(simpleChildSocket);
      close(simpleSocket);
      return 0;
}

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

您还犯了其他错误,例如每次调用accept()时都没有重置clientNameLength变量,在应该使用sockaddr_storage时使用addrinfo作为clientName缓冲区,以及不注意read()的返回值。

尝试更多类似的东西:

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 failedn");
        exit(1);
    }
    addr = res;
    while (NULL != addr)
    {
        simpleSocket = socket(res->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (-1 == simpleSocket)
        {
            fprintf(stderr, "socket failedn");
        }
        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 failedn");
                close(simpleSocket);
            }
        }
        addr = addr->next;
    }
    freeaddrinfo(res);
    if (0 == numServers)
    {
        fprintf(stderr, "no servers are listeningn");
        exit(1);
    }
    struct sockaddr_storage clientName;
    int clientNameLength;
    while (1)
    { 
        returnStatus = poll(simpleSockets, numSockets, -1);
        if (-1 == returnStatus)
        {
            fprintf(stderr, "poll failedn");
            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 failedn");
                        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 connectedn");
                        close(simpleSocket);
                    }
                    else
                        fprintf(stdout, "Client connectedn");
                }
                else
                {
                    returnStatus = read(simpleSockets[n].fd, buffer, sizeof(buffer));
                    if (0 >= returnStatus)
                    {
                        if (0 > returnStatus)
                            fprintf(stdout, "Client error, disconnectingn");
                        else
                            fprintf(stdout, "Client disconnectedn");
                        close(simpleSockets[n].fd);
                        simpleSockets[n].fd = -1;
                        simpleSockets[n].events = 0;
                        simpleSockets[n].revents = 0;
                        continue;
                    }
                    fprintf(stdout, "Message: %.*sn", returnStatus, buffer);
                    write(simpleSockets[n].fd, buffer, returnStatus);     
                }
            }
            if (simpleSockets[n].revents & (POLLERR|POLLHUP|POLLNVAL))
            {
                if (simpleSockets[n].revents & POLLHUP)
                    fprintf(stdout, "Client disconnectedn");
                else if (n >= numServers)
                    fprintf(stdout, "Client error, disconnectingn");
                else
                    fprintf(stdout, "Server error, closingn");
                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()返回的客户端地址将告诉您 IPv4 或 IPv6 客户端是否已连接。

memset(&simpleServer, 0, sizeof simpleServer);
simpleServer.ai_family = AF_INET6;
simpleServer.ai_socktype = SOCK_STREAM;
simpleServer.ai_flags = AI_PASSIVE;
if (0 != getaddrinfo(NULL, argv[1], &simpleServer, &res))
{
    fprintf(stderr, "getaddrinfo failedn");
    exit(1);
}
addr = res;
while (NULL != addr)
{
    simpleSocket = socket(res->ai_family, addr->ai_socktype, addr->ai_protocol);
    if (-1 == simpleSocket)
    {
        fprintf(stderr, "socket failedn");
    }
    else
    {
        n = 0;
        returnStatus = setsockopt(simpleSocket, IPPROTO_IPV6, IPV6_V6ONLY, &n, sizeof(n));
        ...
    }
    addr = addr->next;
}
...

client.c 代码有缺陷,它将完整的 addrinfo 存储在sockaddr_in结构中。RES 属于 addrinfo 型 (http://man7.org/linux/man-pages/man3/getaddrinfo.3.html)

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;

喜欢

case AF_INET: {
    struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr;
    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->ai_addr;
    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;
  }

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

相关内容

  • 没有找到相关文章

最新更新