我创建了一个通过本地网络发送广播消息的客户端:
#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 messagen");
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: %sn", 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 socketn");
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)循环没有机会退出(这并不重要)。
客户端成功发送广播消息。但是如何从服务器端处理它们呢?我尝试了下一种方法(服务器是作为类编写的)。以下是主要方法):
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方法负责初始化套接字,Listen方法负责处理广播消息。
假设服务器有192.168.88.123 ip地址,而客户端在192.168.88.255 ip上发送广播消息。服务器应该从客户端得到消息,我想,但它从来没有真正接收到它们。
我也尝试配置客户端发送消息到255.255.255.255 ip。消息已发送,但服务器未收到。
所以这是代码的错吗?或者与ip相关的内容(如果是这样,那就很奇怪了)?
注:我读了Beej的社交指南。有一个广播的例子,但只是来自客户端。但是我对从服务器端处理这样的消息很感兴趣。
谢谢!
代码看起来很好(尽管,我建议调整一些东西,比如在调用getaddrinfo()
时指定IPPROTO_UDP
协议,并在服务器端包括AI_PASSIVE
标志)。
不是所有的网络路由器默认允许UDP广播。所以检查你的路由器配置,以及你的操作系统防火墙。使用数据包嗅探器,如WireShark,以确保数据包实际上离开发送方的网卡和到达接收方的网卡,他们在传输过程中没有被阻塞。
另外,仔细检查以确保您的客户端套接字和服务器套接字绑定到相同的子网,并且客户端正在发送到该子网的正确广播IP。给定192.168.88.123
的服务器IP,仅当子网掩码为255.255.255.0
时,广播IP将为192.168.88.255
,在您的环境中是这种情况吗?
另外,如果您的服务器绑定到0.0.0.0
(侦听所有本地网卡)而不是绑定到192.168.88.123
(仅侦听1个网卡),检查您是否有相同的问题。