我可以使用inet_pton为我的TCP套接字服务器设置文本地址,例如“device1.local”吗?

时间:2016-08-05 19:28:43

标签: c sockets tcp dns

我在C中编写了一个TCP套接字,它需要使用通过本地网络连接到该地址的移动应用程序:“device1.local:6666”。

我发现使用inet_pton()设置服务器地址的每个示例都有一个IPV4地址,如下所示:192.168.1.34或IPV6地址,如下所示:2001:db8:8714:3a90::12。如果我希望我的客户端telnet到“device1.local”而不是数字或十六进制地址,我指定哪种格式?如何设置我的TCP套接字服务器来回答该地址?

这不起作用:

local_socket = socket(AF_INET6, SOCK_STREAM, 0);
if(local_socket < 0) {
    printf("unable to create socket\n");
    return false;
}
printf("socket created\n");

bzero((void *)&server_address, sizeof(server_address));
server_address.sin6_family = AF_INET6;
int s_code = inet_pton(AF_INET6, "device1.local", &server_address.sin6_addr);
server_address.sin6_port = htons(port_number);

printf("inet_pton returned: %d\n", s_code);

char str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &server_address.sin6_addr, str, INET6_ADDRSTRLEN);
printf("address from inet_ntop(): %s\n", str);

这是输出:

socket created
inet_pton returned: 0
address from inet_ntop(): ::

我意识到我可能表现出对互联网(或本地网络)如何运作的基本缺乏理解。您可以提供给我的任何相关链接,以便我可以了解有关自定义文本地址如何工作的更多信息。

2 个答案:

答案 0 :(得分:1)

  

如果我希望我的客户端telnet到&#34; device1.local&#34;我指定哪种格式?而不是数字或十六进制地址?

inet_pton()将IP地址的文本表示转换为二进制格式(IPv4为in_addr,IPv6为in6_addr等)。您无法使用它将主机名解析为IP地址。为此,您需要使用gethostbyname()(已弃用)或getaddrinfo()(首选)。

以下是gethostbyname()示例:

int connect_to_addr(const sockaddr *addr, int addrlen)
{
    local_socket = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP);
    if (local_socket == -1)
    {
        printf("unable to create socket\n");
        return -1;
    }

    char str[INET6_ADDRSTRLEN];
    unsigned short port;

    switch (addr->sa_family)
    {
        case AF_INET:
        {
            struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
            inet_ntop(AF_INET, &(saddr->sin6_addr), str, sizeof(str));
            port = ntohs(saddr->sin_port);
            break;
        }

        case AF_INET6:
        {
            struct sockaddr_in6 *saddr = (struct sockaddr_in6*)addr;
            inet_ntop(AF_INET6, &(saddr->sin6_addr), str, sizeof(str));
            port = ntohs(saddr->sin6_port);
            break;
        }
    }

    printf("connecting to address: %s on port: %hu\n", str, port);

    if (connect(local_socket, addr, addrlen) != 0)
    {
        printf("unable to connect\n");

        close(local_socket);
        local_socket = -1;

        return 0;
    }

    printf("connected\n");
    return 1;
}

bool connect_to_host(const char *hostname, unsigned short port_number)
{
    local_socket = -1;

    sockaddr_storage server_address;
    bzero(&server_address, sizeof(server_address));

    if (inet_pton(AF_INET6, hostname, &(((struct sockaddr_in6*)&server_address)->sin6_addr)) == 1)
    {
        struct sockaddr_in6 *saddr = (struct sockaddr_in6*)&server_address;

        saddr->sin6_family = AF_INET6;
        saddr->sin6_port = htons(port_number);

        if (connect_to_addr((struct sockaddr*)saddr, sizeof(*saddr)) != 1)
            return false;
    }
    else if (inet_pton(AF_INET, hostname, &(((struct sockaddr_in*)&server_address)->sin_addr)) == 1)
    {
        struct sockaddr_in *saddr = (struct sockaddr_in*)&server_address;

        saddr->sin_family = AF_INET;
        saddr->sin_port = htons(port_number);

        if (connect_to_addr((struct sockaddr*)saddr, sizeof(*saddr)) != 1)
            return false;
    }
    else
    {
        printf("resolving %s\n", hostname);

        struct hostent *host = gethostbyname(hostname);
        if (!host)
        {
            printf("unable to resolve\n");
            return false;
        }

        switch (host->h_addrtype)
        {
            case AF_INET:
            case AF_INET6:
                break;

            default:
                printf("resolved host type is not supported\n");
                return false;
        }

        for(char **addr = host->h_addr_list; *addr != 0; ++addr)
        {
            bzero(&server_address, sizeof(server_address));
            int addrlen;

            switch (host->h_addrtype)
            {
                case AF_INET:
                {
                    struct sockaddr_in *saddr = (struct sockaddr_in*)&server_address;
                    saddr->sin_family = AF_INET;
                    bcopy(*addr, &(saddr->sin_addr), host->h_length);
                    saddr->sin_port = htons(port_number);
                    addrlen = sizeof(*saddr);
                    break;
                }

                case AF_INET6:
                {
                    struct sockaddr_in6 *saddr = (struct sockaddr_in6*)&server_address;
                    saddr->sin6_family = AF_INET6;
                    bcopy(*addr, &(saddr->sin6_addr), host->h_length);
                    saddr->sin6_port = htons(port_number);
                    addrlen = sizeof(*saddr);
                    break;
                }
            }

            if (connect_to_addr((struct sockaddr*)&server_address, addrlen) != 0)
                break;
        }
    }

    return (local_socket != -1);
}

if (connect_to_host("device.local", 6666))
{
    // use local_socket as needed...
    close(local_socket);
}

以下是getaddrinfo()示例:

int connect_to_addr(const sockaddr *addr, int addrlen)
{
    local_socket = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP);
    if (local_socket == -1)
    {
        printf("unable to create socket\n");
        return -1;
    }

    char str[INET6_ADDRSTRLEN];
    unsigned short port;

    switch (addr->sa_family)
    {
        case AF_INET:
        {
            struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
            inet_ntop(AF_INET, &(saddr->sin6_addr), str, sizeof(str));
            port = ntohs(saddr->sin_port);
            break;
        }

        case AF_INET6:
        {
            struct sockaddr_in6 *saddr = (struct sockaddr_in6*)addr;
            inet_ntop(AF_INET6, &(saddr->sin6_addr), str, sizeof(str));
            port = ntohs(saddr->sin6_port);
            break;
        }
    }

    printf("connecting to address: %s on port: %hu\n", str, port);

    if (connect(local_socket, addr, addrlen) != 0)
    {
        printf("unable to connect\n");

        close(local_socket);
        local_socket = -1;

        return 0;
    }

    printf("connected\n");
    return 1;
}

bool connect_to_host(const char* hostname, unsigned short port)
{
    local_socket = -1;

    printf("resolving %s:%hu\n", hostname, port);

    struct addrinfo hints;
    bzero((void *)&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    char service[6];
    bzero(service, sizeof(service));
    sprintf(service, "%hu", port_number);

    struct addrinfo *addrs = 0;
    if (getaddrinfo(hostname, service, &hints, &addrs) != 0)
    {
        printf("unable to resolve\n");
        return false;
    }

    for(struct addrinfo *addr = addrs; addr != 0; addr = addr->ai_next)
    {
        if (connect_to_addr((struct sockaddr*)(addr->ai_sockaddr), addr->ai_addrlen) != 0)
            break;
    }

    freeaddrinfo(addrs);

    return (local_socket != -1);
}

if (connect_to_host("device.local", 6666))
{
    // use local_socket as needed...
    close(local_socket);
}
  

如何设置TCP套接字服务器来回答该地址?

您只需将监听套接字bind()发送到服务器计算机本地的特定IP地址(使用getifaddrs()或等效的API来获取本地IP列表)。或者使用通配符地址(IPv4为INADDR_ANY,IPv6为in6addr_any)绑定到所有可用的本地IP。

客户端的DNS查找将负责将device.local解析为路由到您的服务器计算机的IP地址,如果您的服务器套接字正在侦听该IP,那么它可以accept()传入连接

答案 1 :(得分:1)

.local TLD是为mDNS保留的,如RFC 6762中所述。

因此,device.local工作时,本地系统应设置为device作为其主机名,并应运行Bonjour服务(内置于Windows和OSX,在Linux上以Avahi的形式提供)。

完成后,您的服务器端应用程序根本不需要执行任何特殊操作 - 它应该只是bindINADDR_ANY并接受传入(IPv4)连接在任何界面上。

稍微复杂一点,如果你想通过IPv6服务,那么你通常需要两个监听套接字,第二个监听套接字绑定到in6addr_any

在客户端,getaddrinfo()函数应该能够挂钩到mDNS并相应地将名称解析为正确的IP地址。