【发布时间】:2015-02-17 18:56:36
【问题描述】:
我正在开发一个简单的 UDP 消息传递系统,并且大部分时间都在工作。该结构是有 1 台服务器,它简单地将消息广播到在特定端口上侦听的所有客户端(它仅用于未连接到 Internet 的 LAN 或 ad-hoc 网络,尽管我的测试 PC 目前已插入网络)。我的问题是,每次我发送消息时,客户端都会收到 2 个副本。目前我没有将目标 IP 设置为子网,所以这可能是问题所在:
(注意:我知道我违反了标准的客户端-服务器术语,因为我的“客户端”正在绑定一个特定的端口,但请耐心等待)。
所以这是我的服务器指定目标数据的方式:
d_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(d_socket == -1)
{
printf("\nError in creating socket");
return false;
}
//socket option set to 1 because we are setting the SO_BROADCAST setting to true
//MSDN says to use int for boolean option types.
int l_socket_option = 1;
setsockopt(d_socket,
SOL_SOCKET,
SO_BROADCAST,
(char*)&l_socket_option,
sizeof(int));
//Zero out the broadcast address member to initialize it
memset(&d_broadcast_address,
0,
sizeof(d_broadcast_address));
//Specify that
d_broadcast_address.sin_family = AF_INET;
d_broadcast_address.sin_port = htons(34444);
d_broadcast_address.sin_addr.s_addr = INADDR_BROADCAST;
我的客户这样打开端口:
d_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
//Set port so that calls to recvfrom do not block. Instead recvfrom returns an error if no data is present now.
u_long l_no_block_specifier = 1;
int l_socket_control_result = ioctlsocket(d_socket,
FIONBIO,
&l_no_block_specifier);
if(d_socket == -1)
{
printf("\nError in creating socket");
return false;
}
//Zero out the broadcast address member to initialize it
memset(&d_server_address,
0,
sizeof(d_server_address));
//Specify that
d_server_address.sin_family = AF_INET;
d_server_address.sin_port = htons(34444);
d_server_address.sin_addr.s_addr = INADDR_ANY;
d_address_length = sizeof(d_server_address);
//Bind socket and print an error if bind fails.
if ( bind(d_socket, (SOCKADDR*)&d_server_address, sizeof(SOCKADDR_IN)) != 0 )
{
printf("\nError in binding socket");
}
打开端口后,客户端会生成一个线程来重复读取端口。我编写了一个小型测试应用程序,它只发送一个整数,每个代表消息的序列。该程序的输出如下:
Sending message #0 at time = 4.0 Got message #0 at time = 4.0 Got message #0 at time = 25.0 Sending message #1 at time = 1004.0 Got message #1 at time = 1004.0 Got message #1 at time = 1047.0 Got message #2 at time = 2004.0 Sending message #2 at time = 2004.0 Got message #2 at time = 2064.0 Sending message #3 at time = 3005.0 Got message #3 at time = 3005.0 Got message #3 at time = 3086.0
我已经对其进行了彻底的测试,并且确信程序本身不会意外地为每条消息打印出 2 条通知(除了时间戳显示 2 条收据之间的迟到)。
为什么会这样?我猜这是因为我的测试 PC 已连接到 Internet,因此由于广播,消息通过 2 条不同的路径路由回我自己的 PC。
编辑:
我更改了客户端,使其使用自己的 IP 地址绑定广播端口,而不是指定 INADDR_ANY,这样就停止了重复收据。我还更新了服务器,使其仅在它所属的子网中广播,但我认为这不会对多个收据产生影响。
【问题讨论】:
-
您没有显示您的实际发送或读取代码,但我猜想因为您发送到
INADDR_BROADCAST(255.255.255.255) 而不是特定的子网广播地址,并且正在绑定接收器到INADDR_ANY(0.0.0.0) 而不是特定的 NIC,您可能会从接收器上的不同 NIC 接收每条消息的多个副本。您可以使用WSARecvMsg()而不是recvfrom()来确定哪个网卡实际接收每个数据包。 -
是的,我没有粘贴发送/接收代码,因为那会很多。当我执行 ipconfig 时,我看到我有 2 个 IP 地址,1 个来自“以太网适配器本地连接”,另一个来自“无线 LAN 适配器无线网络连接”。所以我猜测额外的消息来自我的机器上有 2 个 IP 目标以便消息命中?
标签: sockets visual-c++ udp winsock