多播

73次阅读

共计 2558 个字符,预计需要花费 7 分钟才能阅读完成。

多播基于 udp, 让路由器复制数据包传递
基本和 udp 程序一样
不同的地方:
对于发送者重要的 ,
1 发送数据不再直接发送到对端, 而是发送到多播地址, 但端口还是对端的端口 (否则对端套接字无法接受到数据),
这样通过路由器复制再转发, 对端 recvfrom 的 ip 将是路由器
2 多播 ttl (默认 1, 还是修改一下保险一些);
send_addr.sin_addr.s_addr = inet_addr(ipaddr); // 多播地址
send_addr.sin_port = htons(port); // 对端端口

DWORD ttl = 64; // 设置多播 ttl, 默认是 1 有可能不太够用
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,(char*)&ttl,sizeof(ttl));

对于接受方 :
1 由于是多播, 由路由器复制并传递, 所以接受到的 ip 地址一般来说是路由器.
2 需要 bind 一下 port 用于接受数据,
再来就是 socket 需要通过 setsockopt 加入多播组, 否则无法接受多播组的信息
以上 2 点代码示意:

// bind ip,port , 否则无法接受数据
if (bind(sock, (SOCKADDR*)&local_addr, sizeof(local_addr)) == SOCKET_ERROR){
print_error(WSAGetLastError());
return 0;
}

// 多播结构
IP_MREQ join_addr;
join_addr.imr_interface.s_addr = INADDR_ANY; // 加入多播的主机, 一般来说就是本机
join_addr.imr_multiaddr.s_addr = inet_addr(ip); // 需要加入的多播地址

// 通过 IP_ADD_MEMBERSHIP 让 socket 加入多播
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&join_addr, sizeof(join_addr));

全部代码:
multi_sender.c
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);

SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
char ipaddr[16];
int port = 0;
scanf(” %s %d”, ipaddr,&port);
SOCKADDR_IN send_addr;
printf(“ip:%s\n”, ipaddr);

memset(&send_addr, 0, sizeof(send_addr));
send_addr.sin_addr.s_addr = inet_addr(ipaddr); // 发往多播地址
send_addr.sin_port = htons(port); // 接受方的端口
send_addr.sin_family = AF_INET;
DWORD ttl = 64;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,(char*)&ttl,sizeof(ttl));

char buf[100] = “fuck you”;
for (int i = 0; i < 3; ++i){
sendto(sock, buf, strlen(buf), 0, (SOCKADDR*)&send_addr, sizeof(send_addr));
Sleep(1000);
}
closesocket(sock);

WSACleanup();

mutli_recver.c
char ip[16];
unsigned short port = 0;
scanf(” %s %hd”, ip, &port);
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);

SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_addr.s_addr = INADDR_ANY;
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(port); // 接受的端口号

if (bind(sock, (SOCKADDR*)&local_addr, sizeof(local_addr)) == SOCKET_ERROR){
print_error(WSAGetLastError());
return 0;
}

// 加入多播的结构
IP_MREQ join_addr;
join_addr.imr_interface.s_addr = INADDR_ANY; // 本机所有接口
join_addr.imr_multiaddr.s_addr = inet_addr(ip); // 加入多播地址

// 通过 IP_ADD_MEMBERSHIP 加入多播组
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&join_addr, sizeof(join_addr));

char buf[100];
SOCKADDR_IN client_addr;
int cli_len = sizeof(client_addr);
memset(&client_addr, 0, cli_len);
int n = 0;
while (1){
cli_len = sizeof(client_addr);
n = recvfrom(sock, buf, sizeof(buf), 0, (SOCKADDR*)&client_addr, &cli_len);
if (n == 0){
puts(“peer closed”);
break;
}
else if (n == SOCKET_ERROR){
print_error(WSAGetLastError());
break;
}
else {
buf[n] = 0;
printf(“buf:%s, ip from : %s ,port:%d\n”, buf,inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
}

}

WSACleanup();

正文完
 0