getaddrinfo() 返回意外结果

问题描述 投票:0回答:1

我正在尝试学习 C 网络编程,我目前正在使用 echo 服务器,该服务器发送回它收到的任何字符串,我有以下代码:

您可以跳过整个代码部分来解决实际问题

服务器.c

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

#define PORT "8080"
#define BUFFLEN 1024

char *strin(FILE *stream);
void printaddrs(struct addrinfo *res);

int main() {
    struct addrinfo hints, *res;
    // sockfd -> server file descriptor (serverfd)
    int sockfd, clientfd;
    char buffer[BUFFLEN];

    memset(&hints, 0, sizeof(hints)); // Make sure 'hints' is empty
    // Fill in the 'hints' addrinfo structure
    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 (IPv4 or IPv6)
    hints.ai_socktype = SOCK_STREAM; // TCP
    hints.ai_flags = AI_PASSIVE; // Use my address
    printf("'hints' filled successfully!\n");

    if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
        printf("getaddrinfo(): Error!\n");
    }
    printf("getaddrinfo(): Success!\n");

    // print all addresses returned by getaddrinfo()
    printaddrs(res);

    printf("Select an address to bind to: ");
    // strin -> takes input from FILE *stream no matter how long it is!!!
    char *id = strin(stdin);
    if (id == NULL) {
        printf("No input!\n");
        freeaddrinfo(res);
        free(id);
        return 1;
    }
    struct sockaddr_in *ipv4;
    struct sockaddr_in6 *ipv6;
    char addrstr[INET6_ADDRSTRLEN];
    struct addrinfo *p;
    int count = 0, type, family;
    char ipstr[INET6_ADDRSTRLEN], ipver[5];

    for (; res != NULL; res = res->ai_next) {
        if (atoi(id) == count) {
            if (res->ai_family == AF_INET) { // IPv4
                ipv4 = (struct sockaddr_in *) res->ai_addr;
                inet_ntop(res->ai_family, ipv4, addrstr, sizeof(addrstr));

            } else if (res->ai_family == AF_INET6) { // IPv6
                ipv6 = (struct sockaddr_in6 *) res->ai_addr;
                inet_ntop(res->ai_family, ipv6, addrstr, sizeof(addrstr));
            }
            printf("Chosen address: %s\n", addrstr);
            break;
        }
        strcpy(addrstr, "");
        count++;
    }
    free(id);

    if (strcmp(addrstr, "") == 0) {
        printf("Couldn't find entered id.\n");
        freeaddrinfo(res);
        return 1;
    }

    // Create the socket
    sockfd = socket(res->ai_family, res->ai_socktype, 0);
    if (sockfd == -1) {
        printf("socket(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("socket(): Success!\n");

    // Bind the socket to an IP address and a port
    if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
        printf("bind(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("bind(): Success!\n");

    if (listen(sockfd, 5) == -1) {
        printf("listen(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }

    printf("Socket listening on port %s...\n", PORT);

    struct sockaddr *clientaddr;
    char clientaddrstr[INET6_ADDRSTRLEN];
    while (1) {
        clientfd = accept(sockfd, clientaddr, (int *)sizeof(clientaddr));
        if (clientfd == -1) {
            printf("accept(): Failure!\n");
            freeaddrinfo(res);
            return 1;
        }
        printf("accept(): Success!\n");
        inet_ntop(clientaddr->sa_family, clientaddr, clientaddrstr, sizeof(clientaddrstr));
        printf("Accepted a conenction to %s\n", clientaddrstr);
    }
    freeaddrinfo(res);
    return 0;
}

客户端.c

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

#define PORT "8080"
#define BUFFLEN 1024

/** TODO
 * getaddrinfo() to get my address
 */

void printaddrs(struct addrinfo *res);
char *strin(FILE *stream);

int main() {
    printf("Choose an address to connect to: ");
    char *servaddr = strin(stdin);
    // Initialize variables to be used later
    struct addrinfo hints, *res;
    int clientfd;

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

    if (getaddrinfo(servaddr, PORT, &hints, &res) != 0) {
        printf("getaddrinfo(): Failure!\n");
        free(servaddr);
        freeaddrinfo(res);
        return 1;
    }

    printf("getaddrinfo(): Success!\n");

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

    if (clientfd == -1) {
        printf("socket(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("socket(): Success!\n");

    if (connect(clientfd, res->ai_addr, res->ai_addrlen) == -1) {
        printf("connect(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("connect(): Success!\n");
    printf("Connecting to %s...\n", servaddr);
    free(servaddr);

    freeaddrinfo(res);
    return 0;
}

这是 printaddrs() 函数

void printaddrs(struct addrinfo *res) {
    struct addrinfo *p;
    int count = 0;
    char ipstr[INET6_ADDRSTRLEN], ipver[5];
    for (p = res; p != NULL; p = p->ai_next) {
        if (p->ai_family == AF_INET) { // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *) p->ai_addr;
            inet_ntop(p->ai_family, ipv4, ipstr, sizeof(ipstr));
            strcpy(ipver, "ipv4");
        } else if (p->ai_family == AF_INET6) { // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) p->ai_addr;
            inet_ntop(p->ai_family, ipv6, ipstr, sizeof(ipstr));
            strcpy(ipver, "ipv6");
        } else { // Other IP format
            continue;
        }
        printf("%d: %s\t:\t%s\n", count, ipstr, ipver);
        count++;
    }
}

这里是 string()

char *strin(FILE *stream) {
    char *str = NULL;
    size_t size = 0;

    int c;
    int i = 0;
    while (1) {
        c = getc(stream);
        if (stream != stdin) {
            if(c == EOF)
                break;
        } else {
            if(c == EOF || c =='\n')
                break;
        }
        size++;
        str = realloc(str, size * sizeof(char));
        *(str + i) = c;
        i++;
    }
    if (size == 0) {
        return NULL;
    }
    str[size] = '\0';
    return str;
}

问题来了(终于……)

当我运行 server.c 时,getaddrinfo() 返回 *res 结构,它是指向地址链接列表的指针,输出如下所示:

0: 2.0.31.144  :   ipv4 1: a00:1f90::   :   ipv6

我本希望获得本地主机地址,IPv4 为 127.0.0.1,IPv6 为 ::1,但显然不是,当我输入

0
选择 ipv4 地址时:

'hints' filled successfully! getaddrinfo(): Success! 0: 2.0.31.144   :   ipv4 1: a00:1f90::   :   ipv6 Select an address to bind to: 0 Chosen address: 2.0.31.144 socket(): Success! bind(): Success! Socket listening on port 8080...

然后在运行client.c时连接到该地址

Choose an address to connect to: 2.0.31.144 getaddrinfo(): Success! socket(): Success! connect(): Failure!

连接功能不起作用...

有人有什么建议吗?

提前感谢大家的回答,我真的很感激!

我尝试向人工智能寻求解决方案......这不是最明智的举动,我知道...... 我已经快绝望了

c network-programming
1个回答
0
投票

而不是这个:

inet_ntop(p->ai_family, ipv4, ipstr, sizeof(ipstr));

这个:

inet_ntop(p->ai_family, &ipv4->sin_addr, ipstr, sizeof(ipstr));

对 ipv6 结构体的类似处理:

inet_ntop(p->ai_family, &ipv6->sin6_addr, ipstr, sizeof(ipstr));

这将为您的两个地址打印

0.0.0.0
::
。我相信使用 AI_PASSIVE 时这是可以预料到的。这些是“任何”地址,告诉服务器代码绑定到所有适配器 - 这通常是您想要的。

如果从 getaddrsinfo 调用中删除 AI_PASSIVE 标志,您将得到

127.0.0.1
::1
打印

© www.soinside.com 2019 - 2024. All rights reserved.