Multicast Daytime

home Author: Eva M. Castro
eva arroba gsyc.es

First some useful functions that will be used in the multicast server/client examples are explained. The get_addr function returns a sockaddr_storage struct filled with a valid socket address struct for the address, service, family and socket type input parameters. The joinGroup function configures the socket with useful multicast options.

The file mcastutil.h defines the basic multicast socket interface. It defines the following functions:

get_addr fills the sockaddress_storage struct, addr, with information related to the hostname, service, family and socktype.

int
get_addr (const char *hostname,
          const char *service,
          int         family,
          int         socktype,
          struct sockaddr_storage *addr);
joinGroup specifies the address group to be used in the application. It configures the socket with some useful multicast options like loopBack and mcastHop.

int
joinGroup(int sockfd, int loopBack, int mcastHop,
          struct sockaddr_storage *addr);
isMulticast checks if an address is a valid multicast group.

int
isMulticast(struct sockaddr_storage *addr);
File "mcastutil.cpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

#include "mcastutil.h"

int
get_addr (const char *hostname,
          const char *service,
          int         family,
          int         socktype,
          struct sockaddr_storage *addr)
{
    struct addrinfo hints, *res, *ressave;
    int error, sockfd, retval;

    retval = -1;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = family;
    hints.ai_socktype = socktype;

    error = getaddrinfo(hostname, service, &hints, &res);

    if (error != 0) {
        fprintf(stderr,
                "getaddrinfo error:: [%s]\n",
                gai_strerror(error));
        return retval;
    }

    ressave = res;

    sockfd=-1;
    while (res) {
        sockfd = socket(res->ai_family,
                        res->ai_socktype,
                        res->ai_protocol);

        if (!(sockfd < 0)) {
            if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
                close(sockfd);
                memcpy(addr, res->ai_addr, sizeof(*addr);
                retval=0;
                break;
            }

            close(sockfd);
            sockfd=-1;
        }
        res=res->ai_next;
    }

    freeaddrinfo(ressave);

    return retval;
}

int
joinGroup(int sockfd, int loopBack, int mcastTTL,
          struct sockaddr_storage *addr)
{
    int r1, r2, r3, retval;

    retval=-1;

    switch (addr->ss_family) {
        case AF_INET: {
            struct ip_mreq      mreq;

            mreq.imr_multiaddr.s_addr=
                ((struct sockaddr_in *)addr)->sin_addr.s_addr;
            mreq.imr_interface.s_addr= INADDR_ANY;

            r1= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
                           &loopBack, sizeof(loopBack));
            if (r1<0)
                perror("joinGroup:: IP_MULTICAST_LOOP:: ");

            r2= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
                           &mcastTTL, sizeof(mcastTTL));
            if (r2<0)
               perror("joinGroup:: IP_MULTICAST_TTL:: ");

            r3= setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                           (const void *)&mreq, sizeof(mreq));
            if (r3<0)
                perror("joinGroup:: IP_ADD_MEMBERSHIP:: ");

        } break;

        case AF_INET6: {
           struct ipv6_mreq    mreq6;

           memcpy(&mreq6.ipv6mr_multiaddr,
                  &(((struct sockaddr_in6 *)addr)->sin6_addr),
                  sizeof(struct in6_addr));

           mreq6.ipv6mr_interface= 0; // cualquier interfaz

           r1= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 
                          &loopBack, sizeof(loopBack));
           if (r1<0)
               perror("joinGroup:: IPV6_MULTICAST_LOOP:: ");

           r2= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 
                          &mcastTTL, sizeof(mcastTTL));
           if (r2<0)
               perror("joinGroup:: IPV6_MULTICAST_HOPS::  ");

           r3= setsockopt(sockfd, IPPROTO_IPV6,
                          IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
           if (r3<0)
              perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: ");

        } break;

        default:
            r1=r2=r3=-1;
    }

    if ((r1>=0) && (r2>=0) && (r3>=0))
        retval=0;

    return retval;
}


int
isMulticast(struct sockaddr_storage *addr)
{
    int retVal;

    retVal=-1;

    switch (addr->ss_family) {
        case AF_INET: {
            struct sockaddr_in *addr4=(struct sockaddr_in *)addr;
            retVal = IN_MULTICAST(ntohl(addr4->sin_addr.s_addr));
        } break;

        case AF_INET6: {
            struct sockaddr_in6 *addr6=(struct sockaddr_in6 *)addr;
            retVal = IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr);
        } break;

        default:
           ;
    }

    return retVal;
} 
The multicast server is similar to unicast server. It initializes the service, it stops the process (receivefrom) waiting for client connections and it answers immediately after any request.

File "mcastserver.cpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

#include "mcastutil.h"

const char *DAYTIME_PORT="13";

int
main(int argc, char *argv[])
{
    int sockfd, n;
    char *mcastaddr;
    char timeStr[256];
    char b[256];
    struct sockaddr_storage clientaddr, addr;
    socklen_t addrlen;
    time_t now;
    char clienthost[NI_MAXHOST];
    char clientservice[NI_MAXSERV];

    mcastaddr = "FF01::1111";
    if (argc ==2)
        mcastaddr=argv[1];

    memset(&addr, 0, sizeof(addr));

    if (get_addr(mcastaddr, DAYTIME_PORT, PF_UNSPEC, 
                 SOCK_DGRAM, &addr) <0) 
    {
        fprintf(stderr, "get_addr error:: could not find multicast "
                "address=[%s] port=[%s]\n", mcastaddr, DAYTIME_PORT);
        return -1;
    }

    if (isMulticast(&addr)<0) {
        fprintf(stderr, 
                "This address does not seem a multicast address [%s]\n",
                mcastaddr);
        return -1;
    }

    sockfd = socket(addr.ss_family, SOCK_DGRAM, 0);

    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind error:: ");
        close(sockfd);
        return -1;
    }

    if (joinGroup(sockfd, 0 , 8, &addr) <0) {
        close(sockfd);
        return -1;
    }

    addrlen=sizeof(clientaddr);
    for ( ; ;) {
        n = recvfrom(sockfd,
                     b,
                     sizeof(b),
                     0,
                     (struct sockaddr *)&clientaddr,
                     &addrlen);

        if (n <0)
            continue;

        memset(clienthost, 0, sizeof(clienthost));
        memset(clientservice, 0, sizeof(clientservice));

        getnameinfo((struct sockaddr *)&clientaddr, addrlen,
                    clienthost, sizeof(clienthost),
                    clientservice, sizeof(clientservice),
                    NI_NUMERICHOST);

        printf("Received request from host=[%s] port=[%s]\n",
               clienthost, clientservice);

        memset(timeStr, 0, sizeof(timeStr));
        time(&now);
        sprintf(timeStr, "%s", ctime(&now));

        n = sendto(sockfd, timeStr, sizeof(timeStr), 0,
                   (struct sockaddr *)&addr,
                   sizeof(addr));
        if (n<1)
            perror("sendto error:: \n");

    }
    return 0;
}
The multicast client joins the multicast group and waits announcements received by the multicast group.

File "mcastclient.cpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>

#include "mcastutil.h"

const char *DAYTIME_PORT="13";

int
main(int argc, char *argv[])
{
    int sockfd, n;
    char *myhost;
    char timeStr[256];
    char letter;
    struct sockaddr_storage addr, clientaddr;
    int addrlen;
    socklen_t clientaddrlen;


    myhost = "FF01::1111";
    if (argc == 2)
        myhost=argv[1];

    addrlen=sizeof(addr);
    memset(&addr, 0, addrlen);

    get_addr(myhost, DAYTIME_PORT, PF_UNSPEC, SOCK_DGRAM, &addr);

    sockfd = socket(addr.ss_family, SOCK_DGRAM, 0);

    if (bind(sockfd, (struct sockaddr *)&addr, addrlen) <0) {
        perror("bind error:: \n");
        close(sockfd);
        return -1;
    }

    if (joinGroup(sockfd, 0 , 8, &addr) <0) {
        close(sockfd);
        return -1;
    }

    letter = '1';
    n = sendto(sockfd, &letter, sizeof(letter), 0,
              (struct sockaddr *)&addr,
              addrlen);

    if (n<0) {
        perror("sendto error:: ");
        close(sockfd);
        return -1;
    }

    memset(timeStr, 0, sizeof(timeStr));
    clientaddrlen=sizeof(clientaddr);

    n = recvfrom(sockfd,
                 timeStr,
                 sizeof(timeStr),
                 0,
                 (struct sockaddr *)&clientaddr,
                 &clientaddrlen);


    if (n<0) {
        perror("sendto error:: ");
        close(sockfd);
        return -1;
    }

    printf("%s\n", timeStr);

    close(sockfd);
    return 0;
}

home home Author: Eva M. Castro
eva arroba gsyc.es