如何处理广播消息?
How to handle broadcast messages?
我创建了一个通过本地网络发送广播消息的客户端:
#define SERVER_PORT "7777"
int main(int argc, char** argv)
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
if (argc != 3) {
fprintf(stderr,"usage: client ip message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVER_PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("talker: socket");
continue;
}
break;
}
int broadcast = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1)
{
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return 2;
}
std::cout << "type any number to start: ";
int temp = 0;
std::cin >> temp;
while(1)
{
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0, p->ai_addr, p->ai_addrlen)) == -1)
{
std::cout << "[ERROR] sendto error: " << errno << std::endl;
continue;
}
std::cout << "[INFO] sended: " << numbytes << " bytes!" << std::endl;
sleep(5);
}
freeaddrinfo(servinfo);
close(sockfd);
return 0;
}
是的,我正在使用没有机会退出的 while(1) 循环(这无关紧要)。
此客户端成功发送广播消息。但是如何从服务器端处理它们呢?我试着用下一种方法来做(服务器被写成 class。这是主要方法):
virtual void Init() override
{
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
int iRes = -1;
if((iRes = getaddrinfo(m_sIpAddress.c_str(), std::to_string(m_iPort).c_str(), &hints, &m_pAddr)) != 0)
{
std::cout << "getaddrinfo error: " << gai_strerror(iRes) << std::endl;
exit(-1);
}
std::cout << "getaddrinfo success!" << std::endl;
m_iSocket = socket(AF_INET, SOCK_DGRAM, 0);
if(m_iSocket == -1)
{
std::cout << "socket error: " << errno << std::endl;
exit(-1);
}
std::cout << "socket created!" << std::endl;
if((iRes = bind(m_iSocket, m_pAddr->ai_addr, m_pAddr->ai_addrlen)) == -1)
{
std::cout << "bind error: " << errno << std::endl;
exit(-1);
}
std::cout << "binded successfully!" << std::endl;
}
virtual void Listen() override
{
int iNumOfReadBytes = -1;
std::string msg;
msg.resize(1024);
sockaddr_in clientAddr;
socklen_t addrLen = sizeof(sockaddr);
while(true)
{
if((iNumOfReadBytes = recvfrom(m_iSocket, const_cast<char*>(msg.c_str()), 1024-1, 0, reinterpret_cast<sockaddr*>(&clientAddr), &addrLen)) == -1)
{
std::cout << "recvfrom error: " << errno << std::endl;
continue;
}
char ip[16]; //string for ip
inet_ntop(AF_INET, &(clientAddr.sin_addr), ip, 16);
std::cout << "[";
for (int i = 0; i < 16; i++)
{
std::cout << ip[i];
}
std::cout << "]";
std::cout << msg << std::endl;
}
}
init方法负责socket的初始化,Listen方法负责处理广播消息。
假设服务器有 192.168.88.123 IP 地址,而客户端在 192.168.88.255 上发送广播消息 ip.Server 我想应该从客户端接收消息,但它从未真正收到它们。
我也试过配置客户端发送消息到255.255.255.255 ip。消息已发送,但服务器未收到。
那么是代码的错吗?或者有 IP 的东西(如果是这样,那就很奇怪了)?
P.S。我阅读了 Beej 的网络指南。有一个广播示例,但仅来自客户端。但我对从服务器端处理此类消息很感兴趣。
谢谢!
代码看起来不错(不过,有些地方我建议进行调整,比如在调用 getaddrinfo()
时指定 IPPROTO_UDP
协议,并在服务器上包含 AI_PASSIVE
标志侧)。
默认情况下,并非所有网络路由器都允许 UDP 广播。因此,请检查您的路由器配置以及 OS 防火墙。使用数据包嗅探器(如 WireShark)来确保数据包实际上离开了发送方的 NIC 并到达了接收方的 NIC,确保它们在传输过程中没有被阻塞。
此外,仔细检查以确保您的客户端套接字和服务器套接字绑定到同一子网,并且客户端发送到该子网的正确广播 IP。给定 192.168.88.123
的服务器 IP,仅当子网掩码为 255.255.255.0
时,广播 IP 才为 192.168.88.255
,您的环境是否如此?
此外,如果您的服务器绑定到 0.0.0.0
(侦听所有本地 NIC)而不是绑定到 192.168.88.123
(仅侦听 1 个 NIC),请检查您是否遇到同样的问题。
我创建了一个通过本地网络发送广播消息的客户端:
#define SERVER_PORT "7777"
int main(int argc, char** argv)
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
if (argc != 3) {
fprintf(stderr,"usage: client ip message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVER_PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("talker: socket");
continue;
}
break;
}
int broadcast = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1)
{
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return 2;
}
std::cout << "type any number to start: ";
int temp = 0;
std::cin >> temp;
while(1)
{
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0, p->ai_addr, p->ai_addrlen)) == -1)
{
std::cout << "[ERROR] sendto error: " << errno << std::endl;
continue;
}
std::cout << "[INFO] sended: " << numbytes << " bytes!" << std::endl;
sleep(5);
}
freeaddrinfo(servinfo);
close(sockfd);
return 0;
}
是的,我正在使用没有机会退出的 while(1) 循环(这无关紧要)。
此客户端成功发送广播消息。但是如何从服务器端处理它们呢?我试着用下一种方法来做(服务器被写成 class。这是主要方法):
virtual void Init() override
{
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
int iRes = -1;
if((iRes = getaddrinfo(m_sIpAddress.c_str(), std::to_string(m_iPort).c_str(), &hints, &m_pAddr)) != 0)
{
std::cout << "getaddrinfo error: " << gai_strerror(iRes) << std::endl;
exit(-1);
}
std::cout << "getaddrinfo success!" << std::endl;
m_iSocket = socket(AF_INET, SOCK_DGRAM, 0);
if(m_iSocket == -1)
{
std::cout << "socket error: " << errno << std::endl;
exit(-1);
}
std::cout << "socket created!" << std::endl;
if((iRes = bind(m_iSocket, m_pAddr->ai_addr, m_pAddr->ai_addrlen)) == -1)
{
std::cout << "bind error: " << errno << std::endl;
exit(-1);
}
std::cout << "binded successfully!" << std::endl;
}
virtual void Listen() override
{
int iNumOfReadBytes = -1;
std::string msg;
msg.resize(1024);
sockaddr_in clientAddr;
socklen_t addrLen = sizeof(sockaddr);
while(true)
{
if((iNumOfReadBytes = recvfrom(m_iSocket, const_cast<char*>(msg.c_str()), 1024-1, 0, reinterpret_cast<sockaddr*>(&clientAddr), &addrLen)) == -1)
{
std::cout << "recvfrom error: " << errno << std::endl;
continue;
}
char ip[16]; //string for ip
inet_ntop(AF_INET, &(clientAddr.sin_addr), ip, 16);
std::cout << "[";
for (int i = 0; i < 16; i++)
{
std::cout << ip[i];
}
std::cout << "]";
std::cout << msg << std::endl;
}
}
init方法负责socket的初始化,Listen方法负责处理广播消息。
假设服务器有 192.168.88.123 IP 地址,而客户端在 192.168.88.255 上发送广播消息 ip.Server 我想应该从客户端接收消息,但它从未真正收到它们。
我也试过配置客户端发送消息到255.255.255.255 ip。消息已发送,但服务器未收到。
那么是代码的错吗?或者有 IP 的东西(如果是这样,那就很奇怪了)?
P.S。我阅读了 Beej 的网络指南。有一个广播示例,但仅来自客户端。但我对从服务器端处理此类消息很感兴趣。
谢谢!
代码看起来不错(不过,有些地方我建议进行调整,比如在调用 getaddrinfo()
时指定 IPPROTO_UDP
协议,并在服务器上包含 AI_PASSIVE
标志侧)。
默认情况下,并非所有网络路由器都允许 UDP 广播。因此,请检查您的路由器配置以及 OS 防火墙。使用数据包嗅探器(如 WireShark)来确保数据包实际上离开了发送方的 NIC 并到达了接收方的 NIC,确保它们在传输过程中没有被阻塞。
此外,仔细检查以确保您的客户端套接字和服务器套接字绑定到同一子网,并且客户端发送到该子网的正确广播 IP。给定 192.168.88.123
的服务器 IP,仅当子网掩码为 255.255.255.0
时,广播 IP 才为 192.168.88.255
,您的环境是否如此?
此外,如果您的服务器绑定到 0.0.0.0
(侦听所有本地 NIC)而不是绑定到 192.168.88.123
(仅侦听 1 个 NIC),请检查您是否遇到同样的问题。