C ++ UDP服务器仅在获取客户端地址配置时发送消息

时间:2018-08-14 07:30:34

标签: c++ sockets udp

我的目的是创建一个UDP发送方,以将消息广播到特定的IP和端口,而无需任何配置。我定义了struct sockaddr_in si_me变量以在其上设置服务器配置并绑定它:

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");
    }

我期望当我将数据发送到si_me变量中的已配置IP和端口时,每个客户端都可以接收到该消息,但是不会发生,并且在Linux终端上同时使用UDP客户端和nc 127.0.0.1 9090 -u命令,我没有收到任何消息! 我发现我应该为每个客户端创建一个struct sockaddr_in si_other变量。客户端应该向我发送一条消息,以便我可以获取客户端的配置:

if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, (socklen_t*)&slen)) == -1)
        {
            die("recvfrom()");
        }

因此,现在使用si_other变量,我可以成功发送消息。我检查了si_other变量,发现IP与服务器IP相同,但每个客户端的端口都不相同,因此,在不知道目标端口是什么的情况下,我无法向客户端发送数据! / p>

这是完整的源代码:

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;
}

任何人都可以告诉我如何在无需获取客户端消息来配置其目标IP和端口的情况下将源更改为广播消息吗?

2 个答案:

答案 0 :(得分:1)

要发送广播消息,您必须使用级别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,您可以接收发送到广播地址(或发送到属于服务器计算机的单播地址)的数据报。

答案 1 :(得分:0)

完整的源代码中没有bind()调用。 在recvfrom()调用之前需要它。

因此将bind()调用添加到服务器源:

if (bind(s, (struct sockaddr*)&si_me, sizeof(si_me)) == -1)
{
    die("bind");
}

如果要在同一主机中使用客户端和服务器,则应将与服务器不同的端口绑定到客户端。