如何使用getaddrinfo连接到使用外部IP的服务器?

问题描述:

我正在编写一个小型C客户端/服务器应用程序,但使用外部IP地址时无法使连接正常工作。为客户端和服务器的代码是从here拍摄,尤其是客户端做:如何使用getaddrinfo连接到使用外部IP的服务器?

char *default_server_name = "localhost"; 
    char *server_name = NULL; 
    int nport = DEFAULT_DAMA_PORT; 
    char port[15]; 

    // Parse the command line options 
    if (parse_options(argc, argv, &server_name, &nport) < 0) { 
     return -1; 
    } 

    if (server_name == NULL) { 
     server_name = default_server_name; 
    } 

    snprintf(port, 15, "%d", nport); 

    // Connect to the server 
    int client_socket; 
    struct addrinfo hints, *servinfo, *p; 
    int rv; 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    if ((rv = getaddrinfo(server_name, port, &hints, &servinfo)) != 0) { 
     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); 
     exit(1); 
    } 

    for (p=servinfo; p != NULL; p = p->ai_next) { 
     if ((client_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { 
#ifdef DEBUG 
      perror("socket"); 
#endif 
      continue; 
     } 

     if (connect(client_socket, p->ai_addr, p->ai_addrlen) == -1) { 
      close(client_socket); 
#ifdef DEBUG 
      perror("connect"); 
#endif 
      continue; 
     } 

     // Connected succesfully! 
     break; 
    } 

    if (p == NULL) { 
     // The loop wasn't able to connect to the server 
     fprintf(stderr, "Couldn't connect to the server\n."); 
     exit(1); 
    } 

当服务器:

int nport; 
    char port[15]; 
    if (parse_options(argc, argv, &nport) < 0) { 
     return -1; 
    } 

    snprintf(port, 15, "%d", nport); 

    int server_socket; 
    struct addrinfo hints, *servinfo, *p; 
    int rv; 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_flags = AI_PASSIVE; 

    if ((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0) { 
     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); 
     exit(1); 
    } 

    for (p=servinfo; p != NULL; p = p->ai_next) { 
     if ((server_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { 
#ifdef DEBUG 
      perror("socket"); 
#endif 
      continue; 
     } 

     if (bind(server_socket, p->ai_addr, p->ai_addrlen) == -1) { 
      close(server_socket); 
#ifdef DEBUG 
      perror("bind"); 
#endif 
      continue; 
     } 

     // We binded successfully! 
     break; 
    } 

    if (p == NULL) { 
     fprintf(stderr, "failed to bind socket\n"); 
     exit(2); 
    } 

    int pl_one, pl_two; 
    socklen_t pl_one_len, pl_two_len; 
    struct sockaddr_in pl_one_addr, pl_two_addr; 

    if (listen(server_socket, 2) < 0) { 
     fatal_error("Error in syscall listen.", 2); 
    } 

    // Get the two clients connections. 
    pl_one_len = sizeof(pl_one_addr); 
    pl_one = accept(server_socket, 
        (struct sockaddr *)&pl_one_addr, 
        &pl_one_len); 
    if (pl_one < 0) { 
     fatal_error("Error in syscall accept.", 3); 
    } 

    pl_two_len = sizeof(pl_two_addr); 
    pl_two = accept(server_socket, 
        (struct sockaddr *)&pl_two_addr, 
        &pl_two_len); 
    if (pl_two < 0) { 
     fatal_error("Error in syscall accept.", 3); 
    } 

如果我在命令行中指定我的机器的IP,并因此客户端中的server_name被设置为像151.51.xxx.xxx这样的字符串,则套接字无法连接到服务器。还使用127.0.0.1显示了相同的行为,这让我觉得,当文档指出:云在节点名 参数

您感兴趣的主机名。地址可以是主机名称,例如 “www.example.com”,也可以是IPv4或IPv6地址(作为字符串传递)。

这只是开玩笑。

我做错了什么吗?防火墙等问题是否可能导致客户端无法使用IP地址进行连接?

注:我已经搜查了很多关于这个问题,有人说在所有避免使用getaddrinfoINADDR_ANY直接填写,但getaddrinfo文件指出,经过NULLnodename应该已经与INADDR_ANY填写地址,所以我不明白为什么我应该使用旧方法时,新的自动执行此操作。

+0

要使服务器部分按预期工作(为了能够接受连接),必须将调用添加到listen()和accept()。您可能想阅读第9.15章和第9.1章。 – alk 2013-05-04 10:22:57

+0

@alk我明显有调用'listen'和'accept'(否则,如果客户端使用'localhost'作为'server_name',服务器是如何工作的?)。无论如何,我会将它们添加到问题中。 – Bakuriu 2013-05-04 10:34:10

+2

路由器后面的服务器?所需的端口是否打开或转发? – typ1232 2013-05-04 10:45:05

正如注释中所写,您尝试连接到位于路由器后面的网络中的服务器。

127.0.0.1localhost是操作系统的重定向,不会通过路由器。这就是为什么它适合你。

如果指定外部IP地址,则通过路由器进行连接,并且您使用的端口需要在路由器配置中转发。任何正常的家庭互联网终端用户路由器默认阻止任何端口的传入连接。