1. Основные понятия
Обычно существует три способа двухточечной передачи пакетов данных в сети:
Одноадресная рассылка: Метод передачи сообщений данных от отправителя к получателю.
Многоадресная рассылка: Метод передачи сообщений данных от одного отправителя нескольким получателям.
Транслировать: Метод передачи сообщений данных от одного отправителя всем получателям.
Реализация многоадресной рассылки требует установки адреса многоадресной рассылки.
Диапазон адресов многоадресной рассылки в IPv4: от 224.0.0.0 до 239.255.255.255.
2. Конкретный процесс реализации многоадресной рассылки
Отправитель:
шаг.01: Создайте новый сокет 1 для отправки пакетов данных в многоадресную рассылку.
шаг.02: Инициализируйте номер порта многоадресной рассылки в структуре sockaddr_in.
шаг.03: Установите параметры многоадресной рассылки, такие как IP_MULTICAST_LOOP.
шаг.04: Используйте опцию IP_MULTICAST_IF для определения многоадресного интерфейса.
шаг.05: Вызовите интерфейс sendto() для отправки пакетов данных в многоадресную рассылку.
Получатель:
шаг.01: Создайте новый сокет 2 для получения пакетов данных из многоадресной рассылки.
шаг.02: Установите параметр SO_REUSEADDR, чтобы разрешить нескольким получателям получать пакеты данных с одного и того же порта.
шаг.03: Вызовите интерфейсbind(), чтобы привязать сокет 2 к номеру порта, указанному в многоадресной рассылке.
шаг.04: Используйте опцию IP_ADD_MEMBERSHIP, чтобы присоединиться к указанной многоадресной рассылке.
шаг.05: Вызовите интерфейс read() для получения пакетов данных из многоадресной рассылки.
3. Настройки атрибутов многоадресной рассылки
Используйте сокет типа SOCK_DGRAM для реализации процесса многоадресной передачи на основе протокола UDP.
Используйте интерфейс setockopt() для установки свойств, связанных с многоадресной рассылкой.
спецификация интерфейса setockopt:
#include <sys/socket.h>
int setsockopt(int socket,
int level,
int option_name,
const void *option_value,
socklen_t option_len);
#Возвращаемое значение: вернуть 0 после успеха, в противном случае вернуть -1 и установить соответствующий код ошибки.
Интерфейс setockopt() может устанавливать следующие атрибуты для многоадресной рассылки:
IP_ADD_MEMBERSHIP: присоединитесь к указанной многоадресной рассылке.
IP_DROP_MEMBERSHIP: выход из указанной многоадресной рассылки.
IP_MULTICAST_IF: установите интерфейс для отправки пакетов данных в многоадресной рассылке.
IP_MULTICAST_TTL: установите время жизни (TTL) пакетов данных в многоадресной рассылке.
IP_MULTICAST_LOOP: установите, передаются ли обратно копии пакетов данных в многоадресной рассылке.
В-четвертых, полная реализация кода
Демо1 – реализация кода C++.
Отправитель:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
struct in_addr localInterface;
struct sockaddr_in groupSock;
int sd;
char databuf[1024] = "Multicast Data MSG!";
int datalen = sizeof(databuf);
int main (int argc, char *argv[])
{
/*
* Create a datagram socket on which to send.
*/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("opening datagram socket");
exit(1);
}
/*
* Initialize the group sockaddr structure with a
* group address of 225.1.1.1 and port 5555.
*/
memset((char *) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("225.1.1.1");
groupSock.sin_port = htons(5555);
/*
* Disable loopback so you do not receive your own datagrams.
*/
{
char loopch=0;
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP,
(char *)&loopch, sizeof(loopch)) < 0) {
perror("setting IP_MULTICAST_LOOP:");
close(sd);
exit(1);
}
}
/*
* Set local interface for outbound multicast datagrams.
*/
localInterface.s_addr = inet_addr("127.0.0.1");
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&localInterface,
sizeof(localInterface)) < 0) {
perror("setting local interface");
exit(1);
}
/*
* Send a message to the multicast group specified by the
* groupSock sockaddr structure.
*/
if (sendto(sd, databuf, datalen, 0,
(struct sockaddr*)&groupSock,
sizeof(groupSock)) < 0)
{
perror("sending datagram message");
}
else
{
printf("sending datagram message ok! \n");
printf("The message send to multicast server is: \"%s\"\n", databuf);
}
}
Получатель:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
struct sockaddr_in localSock;
struct ip_mreq group;
int sd;
char databuf[1024];
int datalen = sizeof(databuf);
int main (int argc, char *argv[])
{
/*
* Create a datagram socket on which to receive.
*/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("opening datagram socket");
exit(1);
}
/*
* Enable SO_REUSEADDR to allow multiple instances of this
* application to receive copies of the multicast datagrams.
*/
{
int reuse=1;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
(char *)&reuse, sizeof(reuse)) < 0) {
perror("setting SO_REUSEADDR");
close(sd);
exit(1);
}
}
/*
* Bind to the proper port number with the IP address
* specified as INADDR_ANY.
*/
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(5555);;
localSock.sin_addr.s_addr = INADDR_ANY;
if (bind(sd, (struct sockaddr*)&localSock, sizeof(localSock))) {
perror("binding datagram socket");
close(sd);
exit(1);
}
/*
* Join the multicast group 225.1.1.1 on the local 9.5.1.1
*/
group.imr_multiaddr.s_addr = inet_addr("225.1.1.1");
group.imr_interface.s_addr = inet_addr("127.0.0.1");
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&group, sizeof(group)) < 0) {
perror("adding multicast group");
close(sd);
exit(1);
}
/*
* Read from the socket.
*/
datalen = sizeof(databuf);
if (read(sd, databuf, datalen) < 0) {
perror("reading datagram message");
close(sd);
exit(1);
}
else
{
printf("get datagram message ok! \n");
printf("The message from multicast server is: \"%s\"\n", databuf);
}
}
Результаты запуска:
Отправитель:
sending datagram message ok!
The message send to multicast server is: "Multicast Data MSG!"
Получатель:
get datagram message ok!
The message from multicast server is: "Multicast Data MSG!"
Демо2 — реализация кода Python.
Отправитель:
import socket
from time import sleep, time
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
while True:
print "Multicast to group: %s\n" % MCAST_GRP
data = 'Hello World: ' + repr(time()) + '\n'
sock.sendto(data, (MCAST_GRP, MCAST_PORT))
sleep(1)
Получатель:
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print sock.recv(10240)
Результаты запуска:
Отправитель:
Multicast to group: 224.1.1.1
Multicast to group: 224.1.1.1
Multicast to group: 224.1.1.1
Multicast to group: 224.1.1.1
Получатель:
Hello World: 1690308503.613114
Hello World: 1690308503.613114
Hello World: 1690308504.615013
Hello World: 1690308504.615013
5. Справочное чтение
https://www.ibm.com/docs/en/i/7.1?topic=designs-examples-using-multicasting-af-inet
https://os.mbed.com/handbook/Socket
https://subingwen.cn/linux/multicast/index.html