Skip to content

Instantly share code, notes, and snippets.

@hostilefork
Last active July 19, 2024 02:55
Show Gist options
  • Save hostilefork/f7cae3dc33e7416f2dd25a402857b6c6 to your computer and use it in GitHub Desktop.
Save hostilefork/f7cae3dc33e7416f2dd25a402857b6c6 to your computer and use it in GitHub Desktop.
Simple listener and sender for UDP multicast
//
// Simple listener.c program for UDP multicast
//
// Adapted from:
// http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html
//
// Changes:
// * Compiles for Windows as well as Linux
// * Takes the port and group on the command line
//
#ifdef _WIN32
#include <Winsock2.h> // before Windows.h, else Winsock 1 conflict
#include <Ws2tcpip.h> // needed for ip_mreq definition for multicast
#include <Windows.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define MSGBUFSIZE 256
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("Command line args should be multicast group and port\n");
printf("(e.g. for SSDP, `listener 239.255.255.250 1900`)\n");
return 1;
}
char* group = argv[1]; // e.g. 239.255.255.250 for SSDP
int port = atoi(argv[2]); // 0 if error, which is an invalid port
#ifdef _WIN32
//
// Initialize Windows Socket API with given VERSION.
//
WSADATA wsaData;
if (WSAStartup(0x0101, &wsaData)) {
perror("WSAStartup");
return 1;
}
#endif
// create what looks like an ordinary UDP socket
//
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}
// allow multiple sockets to use the same PORT number
//
u_int yes = 1;
if (
setsockopt(
fd, SOL_SOCKET, SO_REUSEADDR, (char*) &yes, sizeof(yes)
) < 0
){
perror("Reusing ADDR failed");
return 1;
}
// set up destination address
//
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY); // differs from sender
addr.sin_port = htons(port);
// bind to receive address
//
if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}
// use setsockopt() to request that the kernel join a multicast group
//
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(group);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (
setsockopt(
fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq)
) < 0
){
perror("setsockopt");
return 1;
}
// now just enter a read-print loop
//
while (1) {
char msgbuf[MSGBUFSIZE];
int addrlen = sizeof(addr);
int nbytes = recvfrom(
fd,
msgbuf,
MSGBUFSIZE,
0,
(struct sockaddr *) &addr,
&addrlen
);
if (nbytes < 0) {
perror("recvfrom");
return 1;
}
msgbuf[nbytes] = '\0';
puts(msgbuf);
}
#ifdef _WIN32
//
// Program never actually gets here due to infinite loop that has to be
// canceled, but since people on the internet wind up using examples
// they find at random in their own code it's good to show what shutting
// down cleanly would look like.
//
WSACleanup();
#endif
return 0;
}
//
// Simple sender.c program for UDP
//
// Adapted from:
// http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html
//
// Changes:
// * Compiles for Windows as well as Linux
// * Takes the port and group on the command line
//
// Note that what this program does should be equivalent to NETCAT:
//
// echo "Hello World" | nc -u 239.255.255.250 1900
#ifdef _WIN32
#include <Winsock2.h> // before Windows.h, else Winsock 1 conflict
#include <Ws2tcpip.h> // needed for ip_mreq definition for multicast
#include <Windows.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> // for sleep()
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("Command line args should be multicast group and port\n");
printf("(e.g. for SSDP, `sender 239.255.255.250 1900`)\n");
return 1;
}
char* group = argv[1]; // e.g. 239.255.255.250 for SSDP
int port = atoi(argv[2]); // 0 if error, which is an invalid port
// !!! If test requires, make these configurable via args
//
const int delay_secs = 1;
const char *message = "Hello, World!";
#ifdef _WIN32
//
// Initialize Windows Socket API with given VERSION.
//
WSADATA wsaData;
if (WSAStartup(0x0101, &wsaData)) {
perror("WSAStartup");
return 1;
}
#endif
// create what looks like an ordinary UDP socket
//
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}
// set up destination address
//
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(group);
addr.sin_port = htons(port);
// now just sendto() our destination!
//
while (1) {
char ch = 0;
int nbytes = sendto(
fd,
message,
strlen(message),
0,
(struct sockaddr*) &addr,
sizeof(addr)
);
if (nbytes < 0) {
perror("sendto");
return 1;
}
#ifdef _WIN32
Sleep(delay_secs * 1000); // Windows Sleep is milliseconds
#else
sleep(delay_secs); // Unix sleep is seconds
#endif
}
#ifdef _WIN32
//
// Program never actually gets here due to infinite loop that has to be
// canceled, but since people on the internet wind up using examples
// they find at random in their own code it's good to show what shutting
// down cleanly would look like.
//
WSACleanup();
#endif
return 0;
}
@tianbengdilie
Copy link

sender in this demo send to the same port this listener, it can be sent to different port. And listener receives message with same group ip

@frenchjam
Copy link

Thanks for this example. Can this be used to have a single sender and multiple receivers, all on the same machine/interface?
I am going to try it to find out, but if someone is listening, please let me know if this is possible.

@Ranner198
Copy link

LOL Love the message in LN 100 of sender.c

@iamfahad43
Copy link

is that really works?
after a day of setting it is still standing there and here is the sender output: " sendto: Success " but nothing at listener???

@Ranner198
Copy link

@iamfahad43 I used it two weeks ago and had no problems, check your routing is what I would recommend. You can try running the application as an admin or enabling the passthrough through your firewall port.

@Goldenkrew3000
Copy link

Thanks :) Couldn't figure out how to use UDP in C

@v-slava
Copy link

v-slava commented Feb 8, 2022

Due to binding to INADDR_ANY in listener here, the listener receives not only multicast traffic but also unicast from any IP.

@pawelsc2
Copy link

setsockopt: No error

@orivee
Copy link

orivee commented Jun 2, 2023

Thanks for this example. Can this be used to have a single sender and multiple receivers, all on the same machine/interface? I am going to try it to find out, but if someone is listening, please let me know if this is possible.

I test, It Works.

@alexveden
Copy link

I faced a situation then the client only receiving data when we explicitly define source IP, just in case if someone has issues with receiving data.

So the block at line https://gist.github.com/hostilefork/f7cae3dc33e7416f2dd25a402857b6c6#file-listener-c-L88 should look like this:

    struct ip_mreq_source mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(group);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    mreq.imr_sourceaddr.s_addr = inet_addr("yyy.xxx.253.232"); // real IP address of a sender
    if ( setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char*) &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt");
        return 1;
    }

@Borjis131
Copy link

I faced a situation then the client only receiving data when we explicitly define source IP, just in case if someone has issues with receiving data.

Probably because you are using IGMPv3, which has support for Source Specific Multicast (SSM), a security feature. If you are having this problem too, you can try to set the IGMP version to use IGMPv2.

In linux it can be done this way:

echo "2" > /proc/sys/net/ipv4/conf/<your_interface>/force_igmp_version

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment