Windows工作站正确设置广播客户端的正确方法是什么?

时间:2014-02-13 02:10:03

标签: c sockets networking winsock broadcast

我一直在尝试为简单的局域网通信创建一个广播客户端,而且我已经制作了一个相当简单的客户端和服务器,它具有基本的数据序列化接口。我一直在弄清楚如何获得我正在使用的任何工作站的“定向广播”地址,以便我可以设置聊天。我是网络新手,所以我在这个领域没有很多经验。我尝试查找示例,但我发现的大多数都不是我想要的。一切都在环回地址上工作,广播是我目前唯一的问题区域。

我从Beej's tutorial中学到了基本的想法,它简要解释了广播数据包的确切内容以及防火墙,网络负载等一些陷阱,也许还有一些不太明显的陷阱。然后我通过一个更加面向winsock的网站Winsock's Programmer's FAQ增强了这些知识,这使得它与我的Windows特定程序更具相关性。

虽然我理解广播背后的想法,但我无法以与工作站无关的方式修改程序,并将数据包从客户端广播到服务器。从我读过的几个SO问题和上面提到的Winsock FAQ中,我发现“获取本地IP地址”的想法并不完全是黑白分明的。由于用户可能有多种不同的网络方式或者您可以使用不同的地址。这是困扰我如何妥善处理的主要问题。

我用来包装此行为的当前函数是:

extern void WINAPI setup_connection( connection_data * data )
{
    char host_name[128] = { 0 };

    struct in_addr host_address;
    struct in_addr broadcast_address;

    if ( gethostname( &host_name[0], sizeof( host_name ) ) == HOST_NOT_FOUND )
    {
        winsock_error( "Getting host name", WSAGetLastError( ), FALSE );
        destroy_winsock_data( data );
        exit( 1 );
    }

    if ( !( data->host_info = gethostbyname( &host_name[0] ) ) )
    {
        winsock_error( "Getting host by name", WSAGetLastError( ), FALSE );
        destroy_winsock_data( data );
        exit( 1 );
    }

    host_address = *( ( struct in_addr * )data->host_info->h_addr );
    broadcast_address.S_un.S_addr = ( host_address.S_un.S_addr & NETMASK ) | ( ~( NETMASK ) );

    data->broadcast_address.sin_family = AF_INET;
    data->broadcast_address.sin_port = htons( PORT );
    data->broadcast_address.sin_addr = broadcast_address;

    return;
} 

主机名似乎是正确的,但gethostbyname显然返回了一个垃圾值。如果我尝试检索返回的IP地址,则表示“地址无效”。由于某些原因,发送调用不会出错,但数据包永远不会发送给我理解。

如果您想知道,我将套接字设置为广播如下:

extern int WINAPI set_socket_to_broadcast( int socket, BOOL setting )
{
   char broadcast = ( setting ? '1' : '0' );

   if ( setsockopt( socket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof( char ) ) == SOCKET_ERROR )
   {
       winsock_error( "Setting Broadcast", WSAGetLastError( ), FALSE );
       return -1;
   }

   return 0;
}

然后,如果你想知道我的connection_data结构是什么,我就这样声明:

typedef struct connection_data
{
    struct sockaddr_in broadcast_address;
    struct hostent * host_info;

    int socket;
} connection_data;

如果您需要更多详细信息,我明天可以再次查看和发布。

2 个答案:

答案 0 :(得分:1)

这里的主要问题是网络掩码不是常量,它是您可以检索的接口的属性。

答案 1 :(得分:1)

请勿使用gethostname()gethostbyname()来检索本地IP地址。它并不总是有效,甚至可以报告虚假信息。不仅如此,它还不能让您访问子网信息,您需要计算广播IP地址。连接到网络的每个IP适配器都有自己的IP和子网,因此您必须使用与实际发送广播的适配器关联的值。使用特定于平台的API来枚举本地IP适配器。在Windows上,使用GetAdaptersInfo()GetAdapterAddresses()。在基于* Nix的平台上,使用getifaddrs()。其中任何一个都会为您提供IP和子网信息(getifaddrs()甚至可以为您提供实际的广播IP)。