Linux下sockets-cpp库的使用

一、sockets-cpp简介

sockets-cpp是一个仅包含头文件的C++库,支持UDP(单播和组播)及TCP(客户端/服务器)套接字。其github地址为:https://github.com/CJLove/sockets-cpp

二、编译sockets-cpp

从github下载sockets-cpp源码,拷贝到Linux系统中,解压缩,进入源码目录:

执行cmake:

cmake . -DCMAKE_INSTALL_PREFIX=$(pwd)/install

由于sockets-cpp依赖fmt库来进行字符串格式化,所以如果系统之前没有安装fmt库,执行cmake时会报错:“By not providing "Findfmt.cmake" in CMAKE_MODULE_PATH this project has asked CMake to find a package configuration file provided by "fmt", but CMake did not find one.”:

执行cmake不是必须的,即使不安装fmt库,也是能使用sockets-cpp的。在sockets-cpp的examples目录中存放着使用示例,其中:clientApp是TCP客户端的使用示例;serverApp是TCP服务器的使用示例;mcastApp是UDP组播的使用示例;unicastApp是UDP单播的使用示例:

以编译mcastApp为例,执行命令:

g++ mcastApp.cpp -o mcastApp -g -I/home/ev/source/sockets-cpp-master/include -I/home/ev/source/sockets-cpp-master/include/sockets-cpp

其中:/home/ev/source/sockets-cpp-master为我电脑上sockets-cpp源码的路径。

编译出mcastApp后,执行下面命令即可启动mcastApp接收udp组播:

./mcastApp -m <multicastAddr> -p <port>

效果如下:

三、修改sockets-cpp源码,使得接收udp数据时能获取发送方ip

以mcastApp为例,打印组播接收到的数据的函数为mcastApp.cpp的onReceiveData函数:

void McastApp::onReceiveData(const char *data, size_t size) {
    std::string str(reinterpret_cast<const char *>(data), size);

    std::cout << "Received: " << str << "\n";
}

可以看到onReceiveData函数是无法获取和打印发送方ip的。这是因为onReceiveData函数是在sockets-cpp/UdpSocket.h的ReceiveTask函数的底层被调用的。而ReceiveTask函数中是通过recv()函数接收udp数据的,该函数没办法返回发送方的地址:

    /**
     * @brief The receive thread for receiving data from UDP peer(s).
     */
    void ReceiveTask() {
        constexpr int64_t USEC_DELAY = 500000;
        while (!m_stop.load()) {
            if (m_fd != INVALID_SOCKET) {
                fd_set fds;
                struct timeval delay {
                    0, USEC_DELAY
                };
                FD_ZERO(&fds);
                FD_SET(m_fd, &fds);
                int selectRet = m_socketCore.Select(m_fd + 1, &fds, nullptr, nullptr, &delay);
                if (selectRet <= 0) {  // select failed or timeout
                    if (m_stop) {
                        break;
                    }
                } else if (FD_ISSET(m_fd, &fds)) {

                    std::array<char, MAX_PACKET_SIZE> msg;
                    ssize_t numOfBytesReceived = m_socketCore.Recv(m_fd, msg.data(), MAX_PACKET_SIZE, 0);
                    // Note: recv() returning 0 can happen for zero-length datagrams
                    if (numOfBytesReceived >= 0) {
                        publishUdpMsg(msg.data(), static_cast<size_t>(numOfBytesReceived));
                    }
                }
            }
        }
    }

所以要想让onReceiveData函数获取和打印发送方ip,我们只要把recv()函数修改为recvfrom函数就可以了,recvfrom函数能返回发送方的地址。即把ReceiveTask函数修改为:


    /**
     * @brief The receive thread for receiving data from UDP peer(s).
     */
    void ReceiveTask() {
        constexpr int64_t USEC_DELAY = 500000;
        while (!m_stop.load()) {
            if (m_fd != INVALID_SOCKET) {

                fd_set fds;
                struct timeval delay {
                    0, USEC_DELAY
                };
                FD_ZERO(&fds);
                FD_SET(m_fd, &fds);
                int selectRet = m_socketCore.Select(m_fd + 1, &fds, nullptr, nullptr, &delay);
                if (selectRet <= 0) {  // select failed or timeout
                    if (m_stop) {
                        break;
                    }
                } else if (FD_ISSET(m_fd, &fds)) {

                    std::array<char, MAX_PACKET_SIZE> msg;
                    //ssize_t numOfBytesReceived = m_socketCore.Recv(m_fd, msg.data(), MAX_PACKET_SIZE, 0);
                    struct sockaddr_in cliaddr;
                    socklen_t len = sizeof(cliaddr);
                    ssize_t numOfBytesReceived = m_socketCore.Recvfrom(m_fd, msg.data(), MAX_PACKET_SIZE, 0, (struct sockaddr*)&cliaddr, &len);
                    // Note: recv() returning 0 can happen for zero-length datagrams
                    
                    char ipstr[INET_ADDRSTRLEN];
                    inet_ntop(AF_INET, &cliaddr.sin_addr, ipstr, sizeof(ipstr));

                    if (numOfBytesReceived >= 0) {
                        
                        publishUdpMsg(ipstr, msg.data(), static_cast<size_t>(numOfBytesReceived));
                    }
                }
            }
        }
    }

publishUdpMsg函数修改为:

    /**
     * @brief Publish a UDP message received from a peer
     *
     * @param msg - pointer to the message data
     * @param msgSize - length of the message data
     */
    void publishUdpMsg(const char *senderIp, const char *msg, size_t msgSize) {
        m_callback.onReceiveData(senderIp, msg, msgSize);
    }

onReceiveData函数修改为:

void McastApp::onReceiveData(const char *senderIp, const char *data, size_t size) {
    std::string str(reinterpret_cast<const char *>(data), size);

    std::cout << "SenderIp: " << std::string(senderIp) << " Received: " << str << std::endl;
}

重新编译,运行,即可发现mcastApp能打印发送方ip了:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值