-
-
Save gtbX/f83daa63d918c4e1667a51a53ab41577 to your computer and use it in GitHub Desktop.
Simple listener and sender for UDP multicast
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 <iphlpapi.h> // needed for GetAdaptersAddresses() | |
#include <Windows.h> | |
#else | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <netdb.h> | |
#include <ifaddrs.h> | |
#include <net/if.h> | |
#include <errno.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 | |
// parse the group address into the appropriate sockaddr | |
// | |
struct addrinfo* info; | |
int gai = getaddrinfo(group, NULL, NULL, &info); | |
if (gai != 0) { | |
fprintf(stderr, "getaddrinfo %s\n", gai_strerror(gai)); | |
return gai; | |
} | |
// create what looks like an ordinary UDP socket | |
// | |
int fd = socket(info->ai_family, 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_storage addr; | |
socklen_t socklen; | |
memset(&addr, 0, sizeof(addr)); | |
addr.ss_family = info->ai_family; | |
if (addr.ss_family == AF_INET) { | |
struct sockaddr_in* inaddr = (struct sockaddr_in*)&addr; | |
inaddr->sin_addr.s_addr = htonl(INADDR_ANY); // differs from sender | |
inaddr->sin_port = htons(port); | |
socklen = sizeof(struct sockaddr_in); | |
} else if (addr.ss_family == AF_INET6) { | |
struct sockaddr_in6* inaddr6 = (struct sockaddr_in6*)&addr; | |
inaddr6->sin6_addr = in6addr_any; // differs from sender | |
inaddr6->sin6_port = htons(port); | |
socklen = sizeof(struct sockaddr_in6); | |
} | |
// bind to receive address | |
// | |
if (bind(fd, (struct sockaddr*) &addr, socklen) < 0) { | |
perror("bind"); | |
return 1; | |
} | |
// iterate through all network interfaces, and join the group on all of them. | |
// joining the group using only the wildcard address will only request messages from the interface of the default route. | |
// | |
#ifdef _WIN32 | |
char ipaddrs[16*1024]; | |
ULONG ipaddrs_len = sizeof(ipaddrs); | |
ULONG err = GetAdaptersAddresses(info->ai_family, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, ipaddrs, &ipaddrs_len); | |
if (err != ERROR_SUCCESS) { | |
fprintf(stderr, "GetAdapterAddresses: %ul\n", err); | |
return err; | |
} | |
for (PIP_ADAPTER_ADDRESSES ipa = (PIP_ADAPTER_ADDRESSES)ipaddrs; ipa != NULL; ipa = ipa->Next) { | |
if (ipa->OperStatus != IfOperStatusUp) | |
continue; // interface must be up | |
if (ipa->FirstUnicastAddress == NULL) | |
continue; // interface must have an address | |
#else | |
struct ifaddrs* ifaddr; | |
if (getifaddrs(&ifaddr) != 0) { | |
perror("getifaddrs"); | |
return 1; | |
} | |
for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { | |
if (ifa->ifa_addr == NULL) | |
continue; // interface must have an address | |
if (ifa->ifa_addr->sa_family != info->ai_family) | |
continue; // address must be same family as group address | |
#endif | |
// use setsockopt() to request that the kernel join a multicast group | |
// | |
if (info->ai_family == AF_INET) { | |
struct ip_mreq mreq; | |
struct sockaddr_in* g_addr = (struct sockaddr_in*)info->ai_addr; | |
mreq.imr_multiaddr.s_addr = g_addr->sin_addr.s_addr; | |
struct sockaddr_in* if_addr = (struct sockaddr_in*) | |
#ifdef _WIN32 | |
ipa->FirstUnicastAddress->Address.lpSockaddr; | |
#else | |
ifa->ifa_addr; | |
#endif | |
mreq.imr_interface.s_addr = if_addr->sin_addr.s_addr; | |
if ( | |
setsockopt( | |
fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq) | |
) < 0 | |
){ | |
if (errno == EADDRINUSE) | |
continue; | |
perror("setsockopt(IP_ADD_MEMBERSHIP)"); | |
return 1; | |
} | |
} else if (info->ai_family == AF_INET6) { | |
struct ipv6_mreq mreq; | |
struct sockaddr_in6* g_addr = (struct sockaddr_in6*)info->ai_addr; | |
mreq.ipv6mr_multiaddr = g_addr->sin6_addr; | |
mreq.ipv6mr_interface = // IPv6 requires interface ID instead of address | |
#ifdef _WIN32 | |
ipa->IfIndex; | |
#else | |
if_nametoindex(ifa->ifa_name); | |
#endif | |
if ( | |
setsockopt( | |
fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*) &mreq, sizeof(mreq) | |
) < 0 | |
){ | |
if (errno == EADDRINUSE) | |
continue; | |
perror("setsockopt(IPV6_JOIN_GROUP)"); | |
return 1; | |
} | |
} | |
} | |
#ifndef _WIN32 | |
freeifaddrs(ifaddr); | |
#endif | |
freeaddrinfo(info); | |
// now just enter a read-print loop | |
// | |
while (1) { | |
char hostbuf[64]; | |
char portbuf[16]; | |
char msgbuf[MSGBUFSIZE]; | |
socklen_t addrlen = sizeof(addr); | |
int nbytes = recvfrom( | |
fd, | |
msgbuf, | |
MSGBUFSIZE, | |
0, | |
(struct sockaddr *) &addr, | |
&addrlen | |
); | |
if (nbytes < 0) { | |
perror("recvfrom"); | |
return 1; | |
} | |
msgbuf[nbytes] = '\0'; | |
gai = getnameinfo((struct sockaddr*)&addr, addrlen, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM); | |
if (gai != 0) { | |
fprintf(stderr, "getnameinfo %s\n", gai_strerror(gai)); | |
return gai; | |
} | |
printf("From [%s]:%s received: %s\n", hostbuf, portbuf, msgbuf); | |
} | |
// | |
// 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. | |
// | |
#ifdef _WIN32 | |
WSACleanup(); | |
#endif | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 <netdb.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, with optional source address\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 | |
// parse the group address into the appropriate sockaddr | |
// | |
struct addrinfo* info; | |
int gai = getaddrinfo(group, NULL, NULL, &info); | |
if (gai != 0) { | |
fprintf(stderr, "getaddrinfo(%s): %s\n", group, gai_strerror(gai)); | |
return gai; | |
} | |
// create what looks like an ordinary UDP socket | |
// | |
int fd = socket(info->ai_family, SOCK_DGRAM, 0); | |
if (fd < 0) { | |
perror("socket"); | |
return 1; | |
} | |
if (argc > 3) { | |
// parse source address from command line | |
char* source = argv[3]; | |
struct addrinfo* source_info; | |
gai = getaddrinfo(source, NULL, NULL, &source_info); | |
if (gai != 0) { | |
fprintf(stderr, "getaddrinfo(%s): %s\n", source, gai_strerror(gai)); | |
return gai; | |
} | |
if (source_info->ai_family != info->ai_family) { | |
fprintf(stderr, "Group (%s) and source (%s) IPs must be the same type\n", group, source); | |
return 1; | |
} | |
if (bind(fd, source_info->ai_addr, source_info->ai_addrlen) != 0) { | |
perror("bind"); | |
return 1; | |
} | |
freeaddrinfo(source_info); | |
} | |
// set up destination address | |
// | |
struct sockaddr* addr = info->ai_addr; | |
socklen_t addrlen = info->ai_addrlen; | |
if (addr->sa_family == AF_INET) { | |
((struct sockaddr_in*)addr)->sin_port = htons(port); | |
} else if (addr->sa_family == AF_INET6) { | |
((struct sockaddr_in6*)addr)->sin6_port = htons(port); | |
} | |
// now just sendto() our destination! | |
// | |
while (1) { | |
char ch = 0; | |
int nbytes = sendto( | |
fd, | |
message, | |
strlen(message), | |
0, | |
addr, | |
addrlen | |
); | |
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 | |
} | |
// | |
// 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. | |
// | |
freeaddrinfo(info); | |
#ifdef _WIN32 | |
WSACleanup(); | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment