我正在尝试根据我为 Winsock 编写(并且有效)的一些代码在 Linux (Ubuntu) 上设置一个小测试应用程序。就目前而言,这只是一个小测试,创建一个套接字(并且看似成功连接),但只是永久挂在 recv() 上,而不是接收数据报。这是一个普通的阻塞套接字。
这是我的创建方法:
// Winsock commodities :)
typedef unsigned int SOCKET;
const unsigned int INVALID_SOCKET = -1;
static SOCKET Connect(const std::string &interfaceIP, const std::string &bindIP, unsigned int port)
{
// Create UDP socket.
SOCKET sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (INVALID_SOCKET != sock_fd)
{
// "Share" socket address.
int sockOpt = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&sockOpt), sizeof(int));
// Enlarge (or embiggen, if you will) recv. buffer.
sockOpt = 1024*512; // 0x80000;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char *>(&sockOpt), sizeof(int));
// Bind to interface(s).
sockaddr_in address;
memset(&address, 0, sizeof(sockaddr_in));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = inet_addr(interfaceIP.c_str()); /* INADDR_ANY */
int addrLen = sizeof(sockaddr_in);
if (0 == bind(sock_fd, reinterpret_cast<sockaddr *>(&address), addrLen))
{
// Join multicast.
ip_mreq multicast;
multicast.imr_multiaddr.s_addr = inet_addr(bindIP.c_str());
multicast.imr_interface = address.sin_addr;
if (0 == setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char *>(&multicast), sizeof(ip_mreq)))
{
// Non-blocking socket, please.
// fcntl(sock_fd, F_SETFL, O_NONBLOCK);
std::cout << "Socket joined multicast: " << bindIP.c_str() << ":" << port << std::endl;
return sock_fd;
}
}
close(sock_fd);
}
std::cout << "Failed to join multicast: " << bindIP.c_str() << ":" << port << std::endl;
return INVALID_SOCKET;
}
进一步测试了一些东西:
很明显我忽略了一些东西。非常感谢帮助:-)
在 Unix 系统中,当使用套接字进行多播时,您应该绑定到 INADDR_ANY,而不是接口。
按接口进行多播过滤(即不从指定接口以外的其他接口接收多播)已经到位,因为您正确填写了 imr_interface。
所以,最后一点系统配置和错误修复起了很大的作用:
a) 作为 root,我必须执行以下操作来禁用反向数据包过滤器: echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter + ethX 也一样。
b) 为 ethX 添加一条虚假路由(route add -net 224.0.0.0.netmask 224.0.0.0 ethX)
c) 将套接字绑定到要加入的组的 IP(否则任何后续套接字都将获取我在该特定端口上加入的所有组的所有数据包)。
d) 将 ip_mreq 结构体的接口成员设置为我们正在接收的适配器的 IP。
然后一切都很好,测试运行快速且平稳(在 800-900 mbit 左右拉动 125 个多播传输流 - 当然这可以更智能,但仍然如此)。谢谢大家的指点。