C ++多播套接字读取挂起Linux

时间:2018-12-20 09:30:50

标签: c++ linux sockets timeout multicast

我重写了一些示例代码,这些代码是我在Internet上找到的,用于在Centos6.9上接收多播数据报,并且可以在一台机器上正常工作

但是当我从另一台计算机上运行它时,它会超时。 (这可能并不重要,但是已经超过了Centos7.5) 最终,我意识到这是防火墙(运行此命令以卸载防火墙)

systemctl stop firewalld

这是我得到的良好输出:

[root@aio-163-124 ~]# ./listener -i 239.0.32.12 -p 11115 -n eth1
allocating udp socket on IPv4 family
address is: 10.70.150.154
binding to:
        multi cast ip: 0.0.0.0 (0)
        multi cast port: 11115 (27435)
joining multicast on:
        multi cast addr: 239.0.32.12 (203423983)
        network interface: 10.70.150.154 (2593539594)
setting read timeout of 30 seconds and 0 microseconds
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
Read: 1316bytes
leaving multicast
closing

我附上完整的代码

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <string>
#include <stdexcept>


const std::string MCAST_GROUP = "239.0.32.12";
const int MCAST_PORT = 11115;
const std::string DST_IFACE_NAME = "enp6s0";
const std::string DST_IFACE_IP = "10.65.150.155";

uint32_t convert_string_ipv4_address_to_uint32_t(const std::string &address)
{
    unsigned char buf[sizeof(struct in6_addr)];
    bzero(buf, sizeof(buf));
    int s = inet_pton(AF_INET, address.c_str(), buf);
    if (s <= 0)
    {
        if (s == 0) std::cerr << "Not in presentation format" << std::endl;
        else
        {
            throw std::runtime_error("inet_pton");
        }
    }
    return *reinterpret_cast<uint32_t *>(buf);
}

class SocketResource
{
    int sd_;
    std::string mcast_group_;
    int mcast_port_;
    std::string dst_iface_name_;
    std::string dst_iface_ip_;

    void convert_iface_name_to_ip_()
    {
        struct ifreq ifr;
        ifr.ifr_addr.sa_family = AF_INET;
        strcpy(ifr.ifr_name, this->dst_iface_name_.c_str());

        if (ioctl(this->sd_, SIOCGIFADDR, &ifr) < 0)
        {
            throw std::runtime_error("Unable to find address");
        }

        this->dst_iface_ip_ = inet_ntoa((reinterpret_cast<struct sockaddr_in *>(&ifr.ifr_addr))->sin_addr);

        /* display result */
        std::cout << "address is: " << this->dst_iface_ip_ << std::endl;
    }

public:
    SocketResource(const std::string &mcast_group, int mcast_port, const std::string &dst_iface_name) :
        mcast_group_(mcast_group)
        , mcast_port_(mcast_port)
        , dst_iface_name_(dst_iface_name)
    {
        /*
         * Create a datagram socket on which to receive.
         */
        std::cout << "allocating udp socket on IPv4 family" << std::endl;
        this->sd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (this->sd_ < 0)
        {
            throw std::runtime_error("opening datagram socket");
        }
        this->convert_iface_name_to_ip_();

    }

    ~SocketResource()
    {
        std::cout << "closing" << std::endl;
        close(this->sd_);
    }

    int set_sock_option(int level, int optname, void *optval, socklen_t optlen)
    {
        return setsockopt(this->sd_, level, optname, optval, optlen);
    }

    void set_sock_opt_reuse()
    {
        /*
         * Enable SO_REUSEADDR to allow multiple instances of this
         * application to receive copies of the multicast datagrams.
         */
        int reuse = 1;
        if (this->set_sock_option(SOL_SOCKET, SO_REUSEADDR,
                                                            reinterpret_cast<void *>(&reuse), sizeof(reuse)) < 0)
        {
            throw std::runtime_error("setting SO_REUSEADDR");
        }
    }

    void bind_to_socket()
    {
        /*
         * Bind to the proper port number with the IP address
         * specified as INADDR_ANY.
         */
        struct sockaddr_in localSock;
        bzero(reinterpret_cast<void *>(&localSock), sizeof(localSock));
        localSock.sin_family = AF_INET;
        localSock.sin_port = htons(this->mcast_port_);
        localSock.sin_addr.s_addr = INADDR_ANY;

        char str[INET_ADDRSTRLEN];
        inet_ntop(localSock.sin_family, reinterpret_cast<char *>(&localSock.sin_addr.s_addr), str, INET_ADDRSTRLEN);

        std::cout
            << "binding to:" << std::endl
            << "\tmulti cast ip: " << str << " (" << localSock.sin_addr.s_addr << ")" << std::endl
            << "\tmulti cast port: " << this->mcast_port_ << " (" << localSock.sin_port << ")" << std::endl;
        if (bind(this->sd_, reinterpret_cast<struct sockaddr *>(&localSock), sizeof(localSock)))
        {
            throw std::runtime_error("binding to address");
        }
    }

    void join_mcast()
    {
        /*
         * Join the multicast group 239.0.32.12 on the local 10.65.150.155
         * interface. Note that this IP_ADD_MEMBERSHIP option must be
         * called for each local interface over which the multicast
         * datagrams are to be received.
         */
        struct ip_mreq group;
        group.imr_multiaddr.s_addr = convert_string_ipv4_address_to_uint32_t(this->mcast_group_);
        group.imr_interface.s_addr = convert_string_ipv4_address_to_uint32_t(this->dst_iface_ip_);
        std::cout
            << "joining multicast on:" << std::endl
            << "\tmulti cast addr: " << this->mcast_group_ << " (" << group.imr_multiaddr.s_addr << ")" << std::endl
            << "\tnetwork interface: " << this->dst_iface_ip_ << " (" << group.imr_interface.s_addr << ")" << std::endl;

        if (this->set_sock_option(IPPROTO_IP, IP_ADD_MEMBERSHIP,
                                                            reinterpret_cast<void *>(&group), sizeof(group)) < 0)
        {
            throw std::runtime_error("adding multicast group");
        }
    }

    void leave_mcast()
    {
        /*
         * Join the multicast group 239.0.32.12 on the local 10.65.150.155
         * interface. Note that this IP_ADD_MEMBERSHIP option must be
         * called for each local interface over which the multicast
         * datagrams are to be received.
         */
        std::cout << "leaving multicast" << std::endl;
        struct ip_mreq group;
        group.imr_multiaddr.s_addr = convert_string_ipv4_address_to_uint32_t(this->mcast_group_);
        group.imr_interface.s_addr = convert_string_ipv4_address_to_uint32_t(this->dst_iface_ip_);
        if (this->set_sock_option(IPPROTO_IP, IP_DROP_MEMBERSHIP,
                                                            reinterpret_cast<void *>(&group), sizeof(group)) < 0)
        {
            throw std::runtime_error("dropping multicast group");
        }
    }

    bool read_timeout()
    {
        fd_set set;
        FD_ZERO(&set); /* clear the set */
        FD_SET(this->sd_, &set); /* add our file descriptor to the set */

        struct timeval timeout;
        timeout.tv_sec = 30;
        timeout.tv_usec = 0;

        std::cout << "setting read timeout of " << timeout.tv_sec << " seconds and " << timeout.tv_usec << " microseconds" << std::endl;
        int rv = select(this->sd_ + 1, &set, NULL, NULL, &timeout);

        if (rv == -1) throw  std::runtime_error("select"); /* an error accured */
        else if (rv == 0) std::cerr << "timeout" << std::endl; /* a timeout occured */

        return rv == -1 || rv == 0;
    }

    void read_from_socket()
    {
        /*
         * Read from the socket.
         */

        const size_t MSGBUFSIZE = 0x4000;
        char databuf[MSGBUFSIZE];
        int size = read(this->sd_, databuf, sizeof(databuf));
        if (size < 0)
        {
            throw std::runtime_error("reading datagram message");
        }
        std::cout << "Read: " << size << "bytes" << std::endl;
    }
};

void print_usage_and_die(const std::string &name)
{

    std::cout <<
        "Usage: " << name << " <options> :" << std::endl
        << "\t[-h]                      This help message." << std::endl
        << "\t[-i multicast ip]         multicast ip." << std::endl
        << "\t[-p port]                 multicast port." << std::endl
        << "\t[-n interface]            ethernet interface." << std::endl
        << "Example: " << name << " -i " << MCAST_GROUP << " -p " << MCAST_PORT << " -n " << DST_IFACE_NAME << std::endl
    ;
    exit(EXIT_FAILURE);
}

void get_options(int argc, char *argv[], std::string &mcast_group, int &mcast_port, std::string &dst_iface_name)
{
    char c = 0;
    while ((c = getopt(argc, argv, "hi:p:s:n:")) != EOF)
    {
        switch (c)
        {
        case 'i':
            mcast_group = optarg;
            break;

        case 'p':
            mcast_port = std::stol(optarg);
            break;

        case 'n':
            dst_iface_name = optarg;
            break;

        case 'h':
        case '?':
            print_usage_and_die(argv[0]);

        default:
            abort();

        }
    }
}

int main(int argc, char *argv[])
{

    /* -------------------------------------------------------------
     *
     * Receive Multicast Datagram code example.
     *
     * -------------------------------------------------------------
     */

    std::string mcast_group = MCAST_GROUP;
    int mcast_port = MCAST_PORT;
    std::string dst_iface_name = DST_IFACE_NAME;
    get_options(argc, argv, mcast_group, mcast_port, dst_iface_name);

    try
    {
        SocketResource socket_resource(mcast_group, mcast_port, dst_iface_name);
        socket_resource.set_sock_opt_reuse();
        socket_resource.bind_to_socket();
        socket_resource.join_mcast();
        if (socket_resource.read_timeout()) return EXIT_FAILURE; /* an error accured */
        for (int i = 0; i < 10; ++i)
        {
            socket_resource.read_from_socket();
        }
        socket_resource.leave_mcast();
    } catch (const std::runtime_error &err)
    {
        perror(err.what());
    } catch (...)
    {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

这是如何编译它:

g++ -std=c++11 -g -o listener listener.cpp

tcpdump命令行:

tcpdump -n -ieth1 "host 10.70.161.248 and host 10.70.162.131"

输出:

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
11:26:46.871261 IP 10.70.162.131.55025 > 10.70.161.248.42761: UDP, length 1316
11:26:51.888180 ARP, Request who-has 10.70.161.248 tell 10.70.162.131, length 46
11:26:51.888194 ARP, Reply 10.70.161.248 is-at fa:16:3e:47:19:4f, length 28
11:54:14.390021 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.392582 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.395470 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.397505 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.400377 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.403287 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.406285 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.408535 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.412468 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.415034 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.417615 IP 10.70.162.131.55025 > 10.70.161.248.11115: UDP, length 1316
11:54:14.417633 IP 10.70.161.248 > 10.70.162.131: ICMP 10.70.161.248 udp port 11115 unreachable, length 556
11:54:19.393190 ARP, Request who-has 10.70.161.248 tell 10.70.162.131, length 46
11:54:19.393213 ARP, Reply 10.70.161.248 is-at fa:16:3e:47:19:4f, length 28

0 个答案:

没有答案