我用不同语言编写了一些示例代码,以便将数据包发送到ipv6链接本地多播地址(ff02 :: fb),但它并不总是有效。 python版本:
#!/usr/bin/env python
MYPORT = 5353
MYGROUP_6 = 'ff02::fb'
MYTTL = 1 # Increase to reach other networks
import time
import struct
import socket
import sys
def main():
sender(MYGROUP_6)
def sender(group):
addrinfo = socket.getaddrinfo(group, None)[0]
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
# Set Time-to-live (optional)
ttl_bin = struct.pack('@i', MYTTL)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin)
while True:
data = repr(time.time())
s.sendto(data + '\0', (addrinfo[4][0], MYPORT))
time.sleep(1)
if __name__ == '__main__':
main()
这个适用于linux(3.13.0-24-generic),但在OS X(10.10优胜美地)上获得错误“无路由到主机”。 然后我写了一个简单的go程序再试一次:
package main
import (
"fmt"
"net"
)
func main() {
addr, err := net.ResolveUDPAddr("udp6", "[ff02::fb]:5353")
if err != nil {
fmt.Printf("ResolveUDPAddr err: %v\n", err)
return
}
if conn, err := net.DialUDP("udp6", nil, addr); err == nil {
if _, err = conn.Write([]byte("hello")); err != nil {
fmt.Printf("Write failed, %v\n", err)
}
} else {
fmt.Printf("DialUDP err: %v\n", err)
}
return
}
这个在linux上得到错误“connect:invalid argument”,并且在OS X上出现与上面的示例相同的错误“没有路由到主机”。最后,我采用了低级语言:
/*
* Examples:
* >sender 224.0.22.1 9210 6000 1000
* >sender ff15::1 2001 65000 1
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h> /* for usleep() */
#define SOCKET int
int mcast_send_socket(char* multicastIP, char* multicastPort, int multicastTTL, struct addrinfo **multicastAddr) {
SOCKET sock;
struct addrinfo hints = { 0 }; /* Hints for name lookup */
/*
Resolve destination address for multicast datagrams
*/
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST;
int status;
if ((status = getaddrinfo(multicastIP, multicastPort, &hints, multicastAddr)) != 0 )
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return -1;
}
/*
Create socket for sending multicast datagrams
*/
if ( (sock = socket((*multicastAddr)->ai_family, (*multicastAddr)->ai_socktype, 0)) < 0 ) {
perror("socket() failed");
freeaddrinfo(*multicastAddr);
return -1;
}
/*
Set TTL of multicast packet
*/
if ( setsockopt(sock,
(*multicastAddr)->ai_family == PF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP,
(*multicastAddr)->ai_family == PF_INET6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL,
(char*) &multicastTTL, sizeof(multicastTTL)) != 0 ) {
perror("setsockopt() failed");
freeaddrinfo(*multicastAddr);
return -1;
}
/*
set the sending interface
*/
if((*multicastAddr)->ai_family == PF_INET) {
in_addr_t iface = INADDR_ANY; /* well, yeah, any */
if(setsockopt (sock,
IPPROTO_IP,
IP_MULTICAST_IF,
(char*)&iface, sizeof(iface)) != 0) {
perror("interface setsockopt() sending interface");
freeaddrinfo(*multicastAddr);
return -1;
}
}
if((*multicastAddr)->ai_family == PF_INET6) {
unsigned int ifindex = 0; /* 0 means 'default interface'*/
if(setsockopt (sock,
IPPROTO_IPV6,
IPV6_MULTICAST_IF,
(char*)&ifindex, sizeof(ifindex)) != 0) {
perror("interface setsockopt() sending interface");
freeaddrinfo(*multicastAddr);
return -1;
}
}
return sock;
}
static void DieWithError(char* errorMessage)
{
fprintf(stderr, "%s\n", errorMessage);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
SOCKET sock;
struct addrinfo *multicastAddr;
char* multicastIP; /* Arg: IP Multicast address */
char* multicastPort; /* Arg: Server port */
char* sendString; /* Arg: String to multicast */
int sendStringLen; /* Length of string to multicast */
int multicastTTL; /* Arg: TTL of multicast packets */
int defer_ms; /* miliseconds to defer in between sending */
int i;
if ( argc < 5 || argc > 6 )
{
fprintf(stderr, "Usage: %s <Multicast Address> <Port> <packetsize> <defer_ms> [<TTL>]\n", argv[0]);
exit(EXIT_FAILURE);
}
multicastIP = argv[1]; /* First arg: multicast IP address */
multicastPort = argv[2]; /* Second arg: multicast port */
sendStringLen = atoi(argv[3]);
defer_ms = atoi(argv[4]);
/* just fill this with some byte */
sendString = calloc(sendStringLen, sizeof(char));
for(i = 0; i<sendStringLen; ++i)
sendString[i]= 's';
multicastTTL = (argc == 6 ? /* Fourth arg: If supplied, use command-line */
atoi(argv[5]) : 1); /* specified TTL, else use default TTL of 1 */
sock = mcast_send_socket(multicastIP, multicastPort, multicastTTL, &multicastAddr);
if(sock == -1 )
DieWithError("mcast_send_socket() failed");
int nr=0;
for (;;) /* Run forever */
{
int* p_nr = (int*)sendString;
*p_nr = htonl(nr);
if ( sendto(sock, sendString, sendStringLen, 0,
multicastAddr->ai_addr, multicastAddr->ai_addrlen) != sendStringLen )
DieWithError("sendto() sent a different number of bytes than expected");
fprintf(stderr, "packet %d sent\n", nr);
nr++;
usleep(defer_ms*1000);
}
/* NOT REACHED */
return 0;
}
但是这个C版本在OS X上也不起作用,它在setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char*)&ifindex, sizeof(ifindex))
上给出错误“无法分配请求的地址”,似乎OS X不支持自动选择界面。我不认为OS X上的某些配置会影响这一点,因为我可以看到Chrome浏览器顺利地在端口5353上向ff02 :: fb发送一些mDNS查询,但我不能发送它,更不用说接收了。
编辑:添加范围ID(en0)使得go&amp; C版本在OS X上运行,但是python(2.7.6)版本仍然抱怨:“没有到主机的路由”。我不希望一些不可移植的代码迭代本地接口列表,然后指定en0(此外,并非所有Mac都有它)。有什么简单的方法吗?