下面的代码无法加入IP多播组地址0:0:0:0:0:ffff:efc0:202,这是转换为IPv6的IPv4地址239.192.2.2。
失败发生在这里:
if (setsockopt(m_socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
{
int rc = WSAGetLastError();
printf("Failed to join multicast group. Error = %d\n", rc);
}
WSAGetLastError()返回10049(“所请求的地址在其上下文中无效”),由于双模套接字应该能够从IPv4和IPv6地址接收UDP数据,所以我觉得很奇怪。
如果我尝试这样做:
if (setsockopt(m_socket, IPPROTO_IPV4, IP_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
{
int rc = WSAGetLastError();
printf("Failed to join multicast group. Error = %d\n", rc);
}
它失败,错误代码rc = 10022(“为setsockopt提供了无效的参数”)
该代码已在Windows 10计算机上的MS Visual Studio 2015中执行。
#define WIN32_LEAN_AND_MEAN
#include <string>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
void main(void)
{
std::string ip = "0:0:0:0:0:ffff:efc0:202";
WSADATA wsaData;
struct ipv6_mreq multicastRequest;
SOCKET m_socket;
int off = 0;
struct addrinfo* tmp = nullptr;
struct addrinfo hints = { 0 };
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return;
}
hints.ai_family = AF_INET6;
hints.ai_flags = AI_NUMERICHOST;
getaddrinfo(ip.c_str(), NULL, &hints, &tmp);
m_socket = socket(AF_INET6, SOCK_DGRAM, 0);
if (setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) < 0)
{
int rc = WSAGetLastError();
printf("Failed to set socket option IPV6_V6ONLY. Error = %d\n", rc);
}
memcpy(&multicastRequest.ipv6mr_multiaddr, &((struct sockaddr_in6*)(tmp->ai_addr))->sin6_addr, sizeof(multicastRequest.ipv6mr_multiaddr));
multicastRequest.ipv6mr_interface = 0;
if (setsockopt(m_socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
{
int rc = WSAGetLastError();
printf("Failed to join multicast group. Error = %d\n", rc);
}
if (setsockopt(m_socket, IPPROTO_IPV4, IP_ADD_MEMBERSHIP (char*)&multicastRequest, sizeof(multicastRequest)) < 0)
{
int rc = WSAGetLastError();
printf("Failed to join multicast group. Error = %d\n", rc);
}
}
答案 0 :(得分:0)
我相当确定您的问题是由尝试向IPv6组播组添加IPv4地址(尽管编码为映射到IPv4的IPv6地址)引起的。
尝试以这种方式使用IPV6_ADD_MEMBERSHIP
会在Linux上导致EINVAL
错误。
相反,请尝试使用适当的setsockopt()
调用将其添加到IPv4组播组(即使用IP_ADD_MEMBERSHIP
),而使用包含本地IPv4地址而不是{{ 1}}包含IPv4映射的地址,例如
struct in_addr
使用基于您的代码(请参见https://pastebin.com/NBZ4KNYf),我已经成功创建了一个双堆栈套接字,并将其分别附加到IPv6组播组和IPv4套接字上,尽管在Linux而不是Windows上。
我能够加入两个dns-sd(RFC 6763)多播组并通过IPv4和IPv6接收数据包。在这两种情况下,struct in6_addr
调用都给我返回了一个memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_flags = AI_NUMERICHOST;
getaddrinfo("224.0.0.251", NULL, &hints, &tmp);
struct sockaddr_in *in4 = (struct sockaddr_in*)tmp->ai_addr;
struct in_addr addr4 = in4->sin_addr;
struct ip_mreqn mreq4 = { 0 };
memcpy(&mreq4.imr_multiaddr, &addr4, sizeof addr4);
mreq4.imr_ifindex = 0;
if (setsockopt(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq4, sizeof(mreq4)) < 0) {
perror("setsockopt(IP_ADD_MEMBERSHIP)");
return -1;
}
格式的地址。