我在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(): ::
我意识到我可能表现出对互联网(或本地网络)如何运作的基本缺乏理解。您可以提供给我的任何相关链接,以便我可以了解有关自定义文本地址如何工作的更多信息。
答案 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的形式提供)。
完成后,您的服务器端应用程序根本不需要执行任何特殊操作 - 它应该只是bind
到INADDR_ANY
并接受传入(IPv4)连接在任何界面上。
稍微复杂一点,如果你想通过IPv6服务,那么你通常需要两个监听套接字,第二个监听套接字绑定到in6addr_any
。
在客户端,getaddrinfo()
函数应该能够挂钩到mDNS并相应地将名称解析为正确的IP地址。