一、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了:



被折叠的 条评论
为什么被折叠?



