Network programming -- multicast and broadcasting

Keywords: network server computer networks udp

reference resources

  1. TCP/IP network programming Yin Shengyu

Multicast and broadcasting

Multicast

Multicast data transmission is based on UDP. The difference from the implementation of UDP server / client is that UDP data is transmitted to a single target, while multicast data is transmitted to a large number of hosts joining (registering) a specific group at the same time

MBone (Multicast Backbone)

Multicast is based on MBone, a virtual network. Virtual network can be understood as a software conceptual network working through special protocols in the network. That is, MBone is not an accessible physical network. It is a necessary virtual network for multicast communication based on physical network and realized by software method

Multicast transmission mode

Data transmission characteristics of multicast:

  1. The multicast server sends data only once for a specific multicast group
  2. Even if data is sent only once, all clients in the group will receive data
  3. The number of multicast groups can be increased arbitrarily within the IP address range
  4. Join a specific group to receive data sent to the multicast group

Multicast group is a class D IP address (224.0.0.0 ~ 239.255.255.255)

Multicast needs to be completed with the help of the router. It only needs to be sent once. The router is responsible for copying files and delivering them to the host. Therefore, multicast is mainly used for real-time transmission of multimedia data

Although multicast communication can be completed in theory, many routers do not support multicast, or even deliberately block multicast due to network congestion. Therefore, in order to complete multicast communication in routers that do not support multicast, Tunneling technology will also be used

Routing and TTL (Time to Live)

In order to deliver multicast packets, TTL must be set. TTL is the main factor determining the transmission distance of data packets. TTL is expressed as an integer and subtracts 1 for each router. When TTL becomes 0, the packet can no longer be delivered and can only be destroyed. Therefore, setting the value of TTL too large will affect the network traffic; If the setting is too small, it will not be delivered to the target

TTL is set through socket options. The protocol layer related to TTL is IPPROTO_IP, option named IP_MULTICAST_TTL. For example, set TTL to 64:

int send_sock;
int time_live = 64;
...
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live));
...

Join multicast group

Joining a multicast group is also done by setting the socket option, which is called IP_ADD_MEMBERSHIP. You can join a multicast group through the following code:

int recv_sock;
struct ip_mreq join_adr;
...
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
...
join_adr.imr_multiaddr.s_addr = "Multicast group address information";
join_adr.imr_interface.s_addr = "Host address information for joining multicast group";
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));
...

Where, ip_mreq structure is defined as follows:

struct ip_mreq
{
	struct in_addr imr_multiaddr;
	struct in_addr imr_interface;
};

First member IMR_ Write the IP address of the added group in multiaddr; Second member imr_interface is the IP address of the host to which the socket joining the group belongs. You can also use INADDR_ANY

Implement multicast Sender and Receiver

Sender and Receiver in multicast replace server and client. The sender is simpler than the Receiver because the Receiver needs to join the group, while the sender only needs to create a UDP socket and send data to the multicast address

(1) Sender example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define TTL 64
#define BUF_SIZE 30
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int send_sock;
    struct sockaddr_in mul_adr;
    int time_live = TTL;
    FILE* fp;
    char buf[BUF_SIZE];
    if (argc != 3)
    {
        printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
        exit(1);
    }

    send_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&mul_adr, 0, sizeof(mul_adr));
    mul_adr.sin_family = AF_INET;
    mul_adr.sin_addr.s_addr = inet_addr(argv[1]);
    mul_adr.sin_port = htons(atoi(argv[2]));

    setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live)); // Specify TTL information, which is a necessary procedure in the Sender
    if ((fp = fopen("news.txt", "r")) == NULL)
    {
        error_handling("fopen() error");
    }

    while (!feof(fp))
    {
        fgets(buf, BUF_SIZE, fp);
        sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&mul_adr, sizeof(mul_adr));  // Based on UDP socket, sendto function is used
        sleep(2);
    }
    fclose(fp);
    close(send_sock);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

(2) Receiver example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int recv_sock;
    int str_len;
    char buf[BUF_SIZE];
    struct sockaddr_in adr;
    struct ip_mreq join_adr;
    if (argc != 3)
    {
        printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
        exit(1);
    }

    recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&adr, 0, sizeof(adr));
    adr.sin_family = AF_INET;
    adr.sin_addr.s_addr = htonl(INADDR_ANY);
    adr.sin_port = htons(atoi(argv[2]));

    if (bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr)) == -1)
    {
        error_handling("bind() error");
    }

    join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]);          // Initialize multicast group address
    join_adr.imr_interface.s_addr = htonl(INADDR_ANY);           // Initialize the IP address of the host to be added

    setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));  // Join multicast group

    while (1)
    {
        str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
        if (str_len < 0)
        {
            break;
        }
        buf[str_len] = 0;
        fputs(buf, stdout);
    }
    close(recv_sock);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

Windows based implementation

It is slightly different from the declaration of only header files under Linux

(1)Sender

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <ws2tcpip.h>

#define TTL 64
#define BUF_SIZE 30
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
    WSADATA wsaData;
    SOCKET hSendSock;
    SOCKADDR_IN mulAdr;
    int timeLive = TTL;
    FILE* fp;
    char buf[BUF_SIZE];

    if (argc != 3)
    {
        printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
        exit(1);
    }
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        ErrorHandling("WSAStartup() error!");
    }

    hSendSock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&mulAdr, 0, sizeof(mulAdr));
    mulAdr.sin_family = AF_INET;
    mulAdr.sin_addr.s_addr = inet_addr(argv[1]);
    mulAdr.sin_port = htons(atoi(argv[2]));

    setsockopt(hSendSock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&timeLive, sizeof(timeLive));
    if ((fp = fopen("news.txt", "r")) == NULL)
    {
        ErrorHandling("fopen() error");
    }
    while (!feof(fp))
    {
        fgets(buf, BUF_SIZE, fp);
        sendto(hSendSock, buf, strlen(buf), 0, (SOCKADDR*)&mulAdr, sizeof(mulAdr));
        Sleep(2000);
    }
    closesocket(hSendSock);
    WSACleanup();
    return 0;
}

void ErrorHandling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

(2)Receiver

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <ws2tcpip.h>

#define BUF_SIZE 30
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
    WSADATA wsaData;
    SOCKET hRecvSock;\
    SOCKADDR_IN adr;
    struct ip_mreq joinAdr;
    char buf[BUF_SIZE];
    int strLen;

    if (argc != 3)
    {
        printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
        exit(1);
    }
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        ErrorHandling("WSAStartup() error!");
    }

    hRecvSock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&adr, 0, sizeof(adr));
    adr.sin_family = AF_INET;
    adr.sin_addr.s_addr = htonl(INADDR_ANY);
    adr.sin_port = htons(atoi(argv[1]));
    if (bind(hRecvSock, (SOCKADDR*)&adr, sizeof(adr)) == SOCKET_ERROR)
    {
        ErrorHandling("bind() error");
    }

    joinAdr.imr_multiaddr.s_addr = inet_addr(argv[1]);
    joinAdr.imr_interface.s_addr = htonl(INADDR_ANY);
    if (setsockopt(hRecvSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&joinAdr, sizeof(joinAdr)) == SOCKET_ERROR)
    {
        ErrorHandling("setsock() error");
    }

    while (1)
    {
        strLen = recvfrom(hRecvSock, buf, BUF_SIZE - 1, 0, NULL, 0);
        if (strLen < 0)
        {
            break;
        }
        buf[strLen] = 0;
        fputs(buf, stdout);
    }
    closesocket(hRecvSock);
    WSACleanup();
    return 0;
}

void ErrorHandling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

radio broadcast

Broadcast is similar to multicast in "sending data to multiple hosts at one time", but the range of data transmission is different. Multicast can receive data as long as it joins a multicast group, even when it spans different networks. Instead, broadcasts can only transmit data to hosts in the same network

Implementation method of broadcast

Broadcasting is also based on UDP. According to the form of IP address used in data transmission, broadcasting is divided into direct broadcast and Local Broadcast. The implementation difference between them mainly lies in IP address

(1) Direct broadcast
For the IP address of direct broadcast, except the network address, all other host addresses are set to 1. That is, data can be transmitted to all hosts in a specific area by direct broadcasting

(2) Local broadcasting
The IP address used in local broadcasting is limited to 255.255.255.255

In fact, the IP address used in data communication is the only difference from the UDP example. However, the socket generated by default will block broadcasting, so you only need to call the setsockopt function to set so_ The broadcast option is set to 1:

int send_sock;
int bcast = 1;
...
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
...
setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&bcast, sizeof(bcast));
...

Implement the Sender and Receiver of broadcast data

(1)Sender

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int send_sock;\
    struct sockaddr_in broad_adr;
    FILE* fp;
    char buf[BUF_SIZE];
    int so_brd = 1;
    if (argc != 3)
    {
        printf("Usage : %s <Broadcast IP> <PORT>\n", argv[0]);
        exit(1);
    }

    send_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&broad_adr, 0, sizeof(broad_adr));
    broad_adr.sin_family = AF_INET;
    broad_adr.sin_addr.s_addr = inet_addr(argv[1]);
    broad_adr.sin_port = htons(atoi(argv[2]));

    setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&so_brd, sizeof(so_brd));
    if ((fp = fopen("news.txt", "r")) == NULL)
    {
        error_handling("fopen() error");
    }
    
    while (!feof(fp))
    {
        fgets(buf, BUF_SIZE, fp);
        sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&broad_adr, sizeof(broad_adr));
        sleep(2);
    }
    close(send_sock);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

(2)Receiver

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char* message);

int main(int argc, char* argv[])
{
    int recv_sock;
    struct sockaddr_in adr;
    int str_len;
    char buf[BUF_SIZE];
    if (argc != 2)
    {
        printf("Usage : %s <PORT>\n", argv[0]);
        exit(1);
    }

    recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&adr, 0, sizeof(adr));
    adr.sin_family = AF_INET;
    adr.sin_addr.s_addr = htonl(INADDR_ANY);
    adr.sin_port = htons(atoi(argv[1]));

    if (bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr)) == -1)
    {
        error_handling("bind() error");
    }
    while (1)
    {
        str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
        if (str_len < 0)
        {
            break;
        }
        buf[str_len] = 0;
        fputs(buf, stdout);
    }
    close(recv_sock);
    return 0;
}

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

Posted by tom92 on Wed, 10 Nov 2021 23:23:26 -0800