c++ - C++ UDP server send message just when get client's address config
问题描述
My purpose is to create a UDP sender to broadcast message to a specific IP and Port, without any config requirment. I defined struct sockaddr_in si_me
variable to set the server config on it and bind it:
si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
{
die("bind");
}
I expected when I send data to configured IP and Port in si_me
variable, every client could receive the message but it doesn't happen and using both UDP client and nc 127.0.0.1 9090 -u
command on Linux terminal, I couldn't receive any message!
I found that for each client I should create a struct sockaddr_in si_other
variable. The client should send me a message so I can get client's config:
if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, (socklen_t*)&slen)) == -1)
{
die("recvfrom()");
}
So now using si_other
variable I can send messages successfully. I checked the si_other
variable and I found the IP is the same with server IP but the Port is different for each client so because of this I couldn't send data to the client without knowing what is the destination Port!
Here is the complete source code:
int main(void)
{
struct sockaddr_in si_me, si_other;
int s, i, slen = sizeof(si_other) , recv_len;
char* msg = "Hellooooo\n";
char buf[BUFLEN] ;
//create a UDP socket
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
die("socket");
}
// zero out the structure
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
std::cout << si_me.sin_addr.s_addr << std::endl ;
std::cout << si_me.sin_port << std::endl ;
std::cout << si_other.sin_addr.s_addr << std::endl ;
std::cout << si_other.sin_port << std::endl ;
if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
{
die("bind");
}
// waiting to recieve client's message
std::cout << "Waiting for start command ... " << std::endl ;
if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, (socklen_t*)&slen)) == -1)
{
die("recvfrom()");
}
std::cout << si_me.sin_addr.s_addr << std::endl ;
std::cout << si_me.sin_port << std::endl ;
std::cout << si_other.sin_addr.s_addr << std::endl ;
std::cout << si_other.sin_port << std::endl ;
long count = 0 ;
while(0)
{
if (sendto(s, msg, strlen(msg), 0, (struct sockaddr*) &si_other, slen) == -1)
{
die("sendto()");
}
std::cout << "Message number " << ++ count << " has been sent" << std::endl ;
}
return 0;
}
So can anyone tell me how should I change the source to broadcast messages without the need of getting client message to configure its destination IP and Port?
解决方案
为了发送广播消息,您必须调用setsockopt
级别SOL_SOCKET
和选项名称SO_BROADCAST
(参见socket(7)
手册页)。所以,在创建套接字之后,(在客户端),你应该有这样的代码:
int turn_on = 1;
retval = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &turn_on, (socklen_t)sizeof(turn_on));
然后,发送者可以在其发送中指定一个广播地址,并且广播域内的任何站点都应该能够接收它。任何服务器都需要绑定到广播地址本身,或者INADDR_ANY
为了接收这样的广播。
这通常的工作方式是:
- 服务器创建套接字,并将其绑定到众所周知的端口
INADDR_ANY
- 客户端创建套接字,不绑定它,但添加
SO_BROADCAST
选项。 - 客户端调用
sendto
缓冲区并sockaddr_in
指定广播地址和众所周知的端口。 - 服务器调用
recvfrom
和接收数据报有效载荷和返回值中的客户端地址 - 服务器使用上面获得的客户端地址进行调用
sendto
(因此客户端使用的端口无关紧要——服务器将知道客户端的端口和 IP 地址)。 - 客户端
recvfrom
在同一个套接字上调用以接收来自服务器的回复消息
通常有两个可用的广播地址:一个特定于子网(由 显示ifconfig
)——例如 192.168.1.255——和全局广播地址——255.255.255.255。
监听INADDR_ANY
应该允许您接收发送到广播地址(或属于服务器机器的单播地址)的数据报。
推荐阅读
- python - ERROR: Command errored out with exit status 1:while installing scikit-surprise on python 3.8
- php - 从 PHP HTTP 客户端调用托管在 Amazon 上的 API 时连接被拒绝
- sql - 当连续序列中的“空心”跟随时,如何标记记录?
- laravel - 在没有 ?page=2 的情况下在 laravel 6 中设置当前页面分页
- sql - SQL Server:根据多个条件在组中搜索
- java - Hibernate InheritanceType.JOINED 阻止创建表
- python - 在 Python 中,这个表达式“(a is b) == ( id(a) == id(b) )” 总是返回 True 吗?
- elixir - 如何使用 protobuf 文件生成长生不老药代码?
- c# - 将excel文件读入datagridview c#
- mysql - 如何将语法结果转换为变量mysql