Różnice między wybraną wersją a wersją aktualną.
| Both sides previous revision Poprzednia wersja | |||
|
sk2:multicast_example [2021/12/20 13:49] jkonczak |
sk2:multicast_example [2025/11/27 19:49] (aktualna) jkonczak |
||
|---|---|---|---|
| Linia 1: | Linia 1: | ||
| ==== Przykład użycia UDP multicast ==== | ==== Przykład użycia UDP multicast ==== | ||
| <code cpp multicast_example.cpp> | <code cpp multicast_example.cpp> | ||
| + | #include <csignal> | ||
| + | #include <cstdio> | ||
| #include <cstdlib> | #include <cstdlib> | ||
| - | #include <cstdio> | ||
| - | #include <csignal> | ||
| - | #include <thread> | ||
| #include <atomic> | #include <atomic> | ||
| + | #include <thread> | ||
| - | #include <unistd.h> | ||
| #include <errno.h> | #include <errno.h> | ||
| #include <error.h> | #include <error.h> | ||
| + | #include <unistd.h> | ||
| - | #include <sys/socket.h> | ||
| - | #include <netinet/ip.h> | ||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||
| + | #include <netinet/ip.h> | ||
| + | #include <sys/socket.h> | ||
| /* UDP multicast example. Compile with threads and C++11 (g++ -pthread --std=c++11) | /* UDP multicast example. Compile with threads and C++11 (g++ -pthread --std=c++11) | ||
| - | * | + | * |
| * Sending to multicast group MCASTIP:MCASTPORT | * Sending to multicast group MCASTIP:MCASTPORT | ||
| * - create a UDP socket | * - create a UDP socket | ||
| * - send a datagram to MCASTIP:MCASTPORT | * - send a datagram to MCASTIP:MCASTPORT | ||
| - | * | + | * |
| * Receiving datagrams sent to a multicast group MCASTIP:MCASTPORT | * Receiving datagrams sent to a multicast group MCASTIP:MCASTPORT | ||
| * - create a UDP socket | * - create a UDP socket | ||
| Linia 28: | Linia 28: | ||
| * - call setsockopt to set option IP_ADD_MEMBERSHIP on level IPPROTO_IP to the aforementioned structure | * - call setsockopt to set option IP_ADD_MEMBERSHIP on level IPPROTO_IP to the aforementioned structure | ||
| * - receive datagrams | * - receive datagrams | ||
| - | * | + | * |
| * setsockopt of IP_ADD_MEMBERSHIP sends an IGMP message "join group" and tells OS that the program waits for messages for the group. | * setsockopt of IP_ADD_MEMBERSHIP sends an IGMP message "join group" and tells OS that the program waits for messages for the group. | ||
| - | * | + | * |
| * More info at: https://tldp.org/HOWTO/Multicast-HOWTO-6.html | * More info at: https://tldp.org/HOWTO/Multicast-HOWTO-6.html | ||
| */ | */ | ||
| - | + | const sockaddr_in groupSockaddr{ | |
| - | const sockaddr_in groupSockaddr { | + | .sin_family = AF_INET, |
| - | .sin_family = AF_INET, | + | .sin_port = htons(6789), |
| - | .sin_port = htons(6789), | + | .sin_addr = {.s_addr = inet_addr("239.255.123.45")} |
| - | .sin_addr = {inet_addr("239.255.123.45")} | + | |
| }; | }; | ||
| - | std::atomic<bool> quitting {false}; | + | std::atomic<bool> quitting{false}; |
| - | void ctrl_c(int){ | + | void ctrl_c(int) { |
| - | if(quitting) | + | if (quitting) |
| - | exit(-1); | + | exit(-1); |
| - | quitting = true; | + | quitting = true; |
| } | } | ||
| void setupSignals(); | void setupSignals(); | ||
| - | int main(int argc, char **argv) { | + | int main() { |
| - | + | ||
| - | // create socket | + | // create socket |
| - | int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | + | int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| - | if(sockfd==-1) error(1,errno, "socket"); | + | if (sockfd == -1) |
| - | + | error(1, errno, "socket"); | |
| - | // set SO_REUSEADDR (or only one app will be allowed to receive multicast messages from the groupIP:port pair) | + | |
| - | // this is one of very few cases when SO_REUSEADDR on UDP has any use | + | // set SO_REUSEADDR (or only one app will be allowed to receive multicast messages from the groupIP:port pair) |
| - | const int one = 1; | + | const int one = 1; |
| - | int res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | + | int res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); |
| - | if(res) error(1,errno, "setsockopt SO_REUSEADDR"); | + | if (res) |
| - | + | error(1, errno, "setsockopt SO_REUSEADDR"); | |
| - | // bind | + | |
| - | sockaddr_in myAddress { | + | // bind |
| - | .sin_family = AF_INET, | + | sockaddr_in myAddress{ |
| - | .sin_port = groupSockaddr.sin_port, | + | .sin_family = AF_INET, |
| - | .sin_addr = {INADDR_ANY} | + | .sin_port = groupSockaddr.sin_port, |
| - | }; | + | .sin_addr = {INADDR_ANY} |
| - | res = bind(sockfd, (sockaddr*) &myAddress, sizeof(myAddress)); | + | }; |
| - | if(res) error(1, errno, "bind"); | + | res = bind(sockfd, (sockaddr *)&myAddress, sizeof(myAddress)); |
| - | + | if (res) | |
| - | // send IGMP join group | + | error(1, errno, "bind"); |
| - | ip_mreqn groupDescription{ | + | |
| - | .imr_multiaddr = groupSockaddr.sin_addr, | + | // send IGMP join group |
| - | .imr_address = {INADDR_ANY}, | + | ip_mreqn groupDescription{ |
| - | .imr_ifindex = 0 | + | .imr_multiaddr = groupSockaddr.sin_addr, |
| - | }; | + | .imr_address = {INADDR_ANY}, |
| - | auto ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &groupDescription, sizeof(groupDescription)); | + | .imr_ifindex = 0 |
| - | if(ret) error(1, errno, "setsockopt IP_ADD_MEMBERSHIP"); | + | }; |
| - | + | auto ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &groupDescription, sizeof(groupDescription)); | |
| - | // handle ctrl+c for gracefull exit | + | if (ret) |
| - | setupSignals(); | + | error(1, errno, "setsockopt IP_ADD_MEMBERSHIP"); |
| - | + | ||
| - | // receive and display ANY packet that arrives | + | // handle ctrl+c for gracefull exit |
| - | std::thread readerThread([&]{ | + | setupSignals(); |
| - | sockaddr_in peerAddr; | + | |
| - | socklen_t peerAddrSize; | + | // receive and display ANY packet that arrives |
| - | char buffer[255]; | + | std::thread readerThread([&] { |
| - | while(true){ | + | sockaddr_in peerAddr; |
| - | peerAddrSize = sizeof(peerAddr); | + | socklen_t peerAddrSize; |
| - | auto readSize = recvfrom(sockfd, buffer, 254, MSG_TRUNC, (sockaddr*) &peerAddr, &peerAddrSize); | + | char buffer[255]; |
| - | if(quitting) break; | + | while (true) { |
| - | if(readSize==-1) error(1,errno, "recvfrom"); | + | peerAddrSize = sizeof(peerAddr); |
| - | if(readSize>254) { error(0,0,"UDP packet too long - truncated from %ld to %d", readSize, 254); readSize=254;} | + | auto readSize = recvfrom(sockfd, buffer, 254, MSG_TRUNC, (sockaddr *)&peerAddr, &peerAddrSize); |
| - | buffer[readSize] = 0; | + | if (quitting) |
| - | printf("<%s:%hu> %s", inet_ntoa(peerAddr.sin_addr), ntohs(peerAddr.sin_port), buffer); | + | break; |
| - | } | + | if (readSize == -1) |
| - | }); | + | error(1, errno, "recvfrom"); |
| - | + | if (readSize > 254) { | |
| - | // read standard input and send it to the group | + | error(0, 0, "UDP packet too long - truncated from %ld to %d", readSize, 254); |
| - | char buffer[255]; | + | readSize = 254; |
| - | while(true){ | + | } |
| - | auto readSize = read(0, buffer, 254); | + | buffer[readSize] = 0; |
| - | if(quitting || !readSize) break; | + | printf("<%s:%hu> %s", inet_ntoa(peerAddr.sin_addr), ntohs(peerAddr.sin_port), buffer); |
| - | if(readSize<0) { error(0, errno, "reading stdin"); break; } | + | } |
| - | + | }); | |
| - | auto res = sendto(sockfd, buffer, readSize, 0, (sockaddr*) &groupSockaddr, sizeof(groupSockaddr)); | + | |
| - | if(res!=readSize) { error(0, errno, "sendto"); break; } | + | // read standard input and send it to the group |
| - | } | + | char buffer[255]; |
| - | + | while (true) { | |
| - | quitting = true; | + | auto readSize = read(0, buffer, 254); |
| - | // leave group | + | if (quitting || !readSize) |
| - | setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &groupDescription, sizeof(groupDescription)); | + | break; |
| - | // close socket AND interrupt all blocked recvfrom | + | if (readSize < 0) { |
| - | shutdown(sockfd, SHUT_RDWR); | + | error(0, errno, "reading stdin"); |
| - | close(sockfd); | + | break; |
| - | // wait for child | + | } |
| - | readerThread.join(); | + | |
| - | + | auto res = sendto(sockfd, buffer, readSize, 0, (sockaddr *)&groupSockaddr, sizeof(groupSockaddr)); | |
| - | return 0; | + | if (res != readSize) { |
| + | error(0, errno, "sendto"); | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | quitting = true; | ||
| + | // leave group | ||
| + | setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &groupDescription, sizeof(groupDescription)); | ||
| + | // close socket AND interrupt all blocked recvfrom | ||
| + | shutdown(sockfd, SHUT_RDWR); | ||
| + | close(sockfd); | ||
| + | // wait for child | ||
| + | readerThread.join(); | ||
| + | |||
| + | return 0; | ||
| } | } | ||
| - | void setupSignals(){ | + | void setupSignals() { |
| - | struct sigaction action; | + | struct sigaction action; |
| - | + | ||
| - | // read sigint handler description | + | // read sigint handler description |
| - | auto res = sigaction(SIGINT, nullptr, &action); | + | auto res = sigaction(SIGINT, nullptr, &action); |
| - | if(res) error(1, errno, "sigaction read"); | + | if (res) |
| - | + | error(1, errno, "sigaction read"); | |
| - | // set new interrupt handler | + | |
| - | action.sa_handler = ctrl_c; | + | // set new interrupt handler |
| - | // disable SA_RESTART flag to exit blocking IO upon a signal | + | action.sa_handler = ctrl_c; |
| - | action.sa_flags &= ~SA_RESTART; | + | // disable SA_RESTART flag to exit blocking IO upon a signal |
| - | + | action.sa_flags &= ~SA_RESTART; | |
| - | // update sigint handler description | + | |
| - | res = sigaction(SIGINT, &action, nullptr); | + | // update sigint handler description |
| - | if(res) error(1, errno, "sigaction modify"); | + | res = sigaction(SIGINT, &action, nullptr); |
| + | if (res) | ||
| + | error(1, errno, "sigaction modify"); | ||
| } | } | ||
| </code> | </code> | ||