Сетевое программирование Linux — программирование многоадресной рассылки UDP
Сетевое программирование Linux — программирование многоадресной рассылки UDP

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:

Язык кода:javascript
копировать
#include <sys/socket.h>
int setsockopt(int socket, 
               int level, 
               int option_name,
               const void *option_value, 
               socklen_t option_len);

#Возвращаемое значение: вернуть 0 после успеха, в противном случае вернуть -1 и установить соответствующий код ошибки.

Интерфейс setockopt() может устанавливать следующие атрибуты для многоадресной рассылки:

Язык кода:javascript
копировать
IP_ADD_MEMBERSHIP: присоединитесь к указанной многоадресной рассылке.
IP_DROP_MEMBERSHIP: выход из указанной многоадресной рассылки.
IP_MULTICAST_IF: установите интерфейс для отправки пакетов данных в многоадресной рассылке.
IP_MULTICAST_TTL: установите время жизни (TTL) пакетов данных в многоадресной рассылке.
IP_MULTICAST_LOOP: установите, передаются ли обратно копии пакетов данных в многоадресной рассылке.

В-четвертых, полная реализация кода

Демо1 – реализация кода C++.

Отправитель:

Язык кода:javascript
копировать
#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);
  }
}

Получатель:

Язык кода:javascript
копировать
#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);
  }
}

Результаты запуска:

Отправитель:

Язык кода:javascript
копировать
sending datagram message ok!
The message send to multicast server is: "Multicast Data MSG!"

Получатель:

Язык кода:javascript
копировать
get datagram message ok!
The message from multicast server is: "Multicast Data MSG!"

Демо2 — реализация кода Python.

Отправитель:

Язык кода:javascript
копировать
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)

Получатель:

Язык кода:javascript
копировать
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)

Результаты запуска:

Отправитель:

Язык кода:javascript
копировать
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

Получатель:

Язык кода:javascript
копировать
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

boy illustration
Эффективная обработка запланированных задач: углубленное изучение секретов библиотеки APScheduler на Python
boy illustration
Рекомендации по облегченному артефакту развязки внутренних компонентов Spring Event (событие Spring)
boy illustration
Go: Лесоруб-лесоруб на колесах Введение
boy illustration
Основы серверной разработки: технология кэширования, которую должен освоить каждый программист
boy illustration
Java Advanced Collections TreeSet: что это такое и зачем его использовать?
boy illustration
Оказывается, у команды go build столько знаний
boy illustration
Node.js
boy illustration
Анализ исходного кода, связанный с запланированными задачами версии ruoyi-vue (7), то есть анализ модуля ruoyi-quartz.
boy illustration
Вход в систему с помощью скан-кода WeChat (1) — объяснение процесса входа в систему со скан-кодом, получение авторизованного QR-кода для входа.
boy illustration
HikariPool-1 — обнаружено отсутствие потока или скачок тактовой частоты, а также конфигурация источника данных Hikari.
boy illustration
Сравнение высокопроизводительной библиотеки JSON Go
boy illustration
Простое руководство по извлечению аудио с помощью FFmpeg
boy illustration
Подсчитайте количество строк кода в проекте
boy illustration
Spring Boot элегантно реализует многопользовательскую архитектуру: концепции и практика
boy illustration
Как интегрировать функцию оповещения корпоративного WeChat в систему планирования xxl-job
boy illustration
SpringBoot интегрирует отправку сообщений через веб-сокет в режиме реального времени
boy illustration
Краткий анализ основных библиотек журналов в Go: узнайте, как интегрировать функции вращения и резки бревен на уровне проектирования.
boy illustration
Реализация API-шлюза с нуля-Golang
boy illustration
[Разговорный сайт] Как Springboot получает значения свойств из файлов конфигурации yml или свойств
boy illustration
Spring Boot — синхронные события приложения против асинхронных событий публикации и подписки. Практический бой
boy illustration
Spring Boot использует Swagger3 для создания документов интерфейса API.
boy illustration
[1269] Использование Gunicorn для развертывания проектов flask.
boy illustration
Краткое изложение 10 способов регистрации bean-компонентов в SpringBoot
boy illustration
Flask Learning-9. 2 способа включения режима отладки (debug mode).
boy illustration
Руководство по настройке самостоятельного сервера для Eudemons Parlu
boy illustration
40 вопросов для собеседований по SpringBoot, которые необходимо задавать на собеседованиях! При необходимости ответьте на вопросы для собеседования SpringBoot [предлагаемый сборник] [легко понять]
boy illustration
Через два года JVM может быть заменен GraalVM.
boy illustration
Разрешение циклических зависимостей Spring Bean: существует ли неразрешимая циклическая ссылка?
boy illustration
Разница между промежуточным программным обеспечением ASP.NET Core и фильтрами
boy illustration
[Серия Foolish Old Man] Ноябрь 2023 г. Специальная тема Winform Control Элемент управления DataGridView Подробное объяснение