我正试图把我的注意力集中在套接字编程上,并且遇到了一些意想不到的行为(对我来说)。
当我试图将数据发送到"localhost“并将addrinfo.ai_family设置为AF_INET时,我发送的消息不会从客户机进程传递到主机进程(recvfrom()不返回)。如果我把它设置为AF_INET6,一切都很好。与AF_UNSPEC相同,在这种情况下,它将选择IPv6 addrinfo (列表中的第一个)。当然,主机和客户端都使用相同的ai_family。
我也尝试过这样做,从beej网络编程指南粘贴的代码副本具有相同的结果。我在用DGRAM套接字。
我试着从另一台pc上连接,我得到了相反的结果,IPv4工作得很好,IPv6没有。我想,这可能是由于我使用‘6至4网关’。我真的不知道这意味着什么。
这个问题与我自己的机器有关,因为代码确实在我测试过的另一台机器上的IPv4上工作。我不能说这是发送还是接收的问题。
有什么可以阻止我使用AF_INET套接字向本地主机发送或接收数据?
我在一台windows7 64位机器上用MingW编译。如果有任何不同,我将为具有不同参数的主机和客户端进程运行相同的程序。我同时运行了发行版和调试程序(所以它不是同一个程序两次),但是得到了相同的结果。
如果这被认为是一个愚蠢的问题,请提前致谢并道歉。
代码:
typedef struct addrinfo addrinfo_t;
typedef struct sockaddr_storage sockaddr_storage_t;
typedef struct sockaddr_in sockaddr_in_t;
typedef struct sockaddr_in6 sockaddr_in6_t;
void connect_to_server(const char* server_name, const char* message)
{
int status;
init_networking();
addrinfo_t hints;
addrinfo_t* res;
memset(&hints, 0, sizeof(addrinfo_t));
hints.ai_family = AF_INET; //or AF_INET6
hints.ai_socktype = SOCK_DGRAM;
if ((status = getaddrinfo(server_name, "4950", &hints, &res)) != 0)
{
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == -1)
{
fprintf(stderr, "could not create a socket, errno: %u\n", errno);
exit(1);
}
int bytes_sent = sendto(s, message, strlen(message), 0, res->ai_addr, res->ai_addrlen);
close(s);
printf("Sent %i bytes to port %i\n", bytes_sent, ((sockaddr_in_t*)res->ai_addr)->sin_port);
freeaddrinfo(res);
}
void setup_server()
{
int status;
init_networking();
addrinfo_t hints;
addrinfo_t* res;
memset(&hints, 0, sizeof(addrinfo_t));
hints.ai_family = AF_INET; //or AF_INET6
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((status = getaddrinfo(NULL, "4950", &hints, &res)) != 0)
{
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == -1)
{
fprintf(stderr, "could not create a socket, errno: %u\n", errno);
exit(1);
}
//Bind the socket to own address (mostly the port number contained in the address)
if (bind(s, res->ai_addr, res->ai_addrlen) < 0)
{
fprintf(stderr, "failed to bind, errno: %u\n", errno);
exit(1);
}
freeaddrinfo(res);
const size_t read_buffer_size = 1024;
void* read_buffer = malloc(read_buffer_size);
sockaddr_storage_t peer_address;
int peer_address_length = sizeof(sockaddr_storage_t);
sockaddr_storage_t own_sock_addr;
int own_sock_addr_len = sizeof(sockaddr_storage_t);
getsockname(s, (struct sockaddr*)&own_sock_addr, &own_sock_addr_len);
printf("Listening on port %i\n", ((sockaddr_in_t*)&own_sock_addr)->sin_port);
int bytes_received = recvfrom(s,
read_buffer,
read_buffer_size-1,
0,
(struct sockaddr*)&peer_address,
&peer_address_length );
printf("Received %i byte message:\n%s\n", bytes_received, (char*)read_buffer);
}发布于 2013-08-07 18:49:07
AF_INET代表IPv4,AF_INET6代表IPv6。发送IPv4数据报时,接收方必须使用IPv4套接字或IPv6双栈套接字 (同时接受IPv4和IPv6流量的IPv6套接字)在目标IP/端口上接收数据。发送IPv6数据报时,接收方必须使用IPv6套接字接收数据。否则,数据报将被忽略,因此听起来一台机器使用的是IPv6套接字,它忽略了您的IPv4数据报,而另一台机器使用的是IPv4套接字,该套接字忽略了您的IPv6数据报。
当您调用getaddrinfo()时,您将AF_UNSPEC指定为客户机和服务器中的地址家族。AF_UNSPEC告诉getaddrinfo()您同时需要IPv4和IPv6地址,因此它返回一个链接列表,该列表可能包含所有可用的IPv4 和 IPv6地址的多个条目。在服务器端,您只为列表中的第一个条目(可能是IPv4或IPv6 )创建一个侦听套接字。在客户端,您只为列表中的第一个条目(可能是IPv4或IPv6 )创建一个发送套接字。因此,在这两个操作中使用的实际地址家族都是随机的,有时可能不匹配。
在服务器端,您需要:
AF_INET或AF_INET6,而不是AF_UNSPEC,然后对客户机进行相应的编码以进行匹配。addrinfo列表,为每个条目创建单独的侦听套接字。这样,客户端就可以向服务器监听的任何IP/端口家族发送数据。AF_INET6,然后在其上启用双栈功能 (仅限Vista+),以便它们能够同时接收IPv4和IPv6数据报。然后,您必须注意sockaddr报告的recvfrom()返回的地址系列,以便知道任何给定的数据报是否使用IPv4或IPv6。在客户端,您需要直接使用AF_INET或AF_INET6,而不是AF_UNSPEC,这取决于服务器实际侦听的内容。对UDP客户端套接字使用AF_UNSPEC是没有意义的(对于TCP客户端套接字确实是有意义的),除非您正在用ack实现对每个数据报的答复。否则,客户端就无法知道服务器是接受IPv4还是IPv6数据报(除非用户告诉应用程序)。使用ack,客户端可以循环遍历返回的addrinfo列表,将数据报发送到列表中的一个条目,等待几秒钟等待ack,如果未收到,则继续到列表中的下一个条目,在需要时重复,直到ack实际到达或列表耗尽为止。
https://stackoverflow.com/questions/18108797
复制相似问题