最近在项目中遇到一个关于UDP广播的问题,顺藤摸瓜总算找到了原因所在,在此记录一下也分享给遇到相同问题的朋友参考。
(1)项目背景:PC软件需要发送UDP广播包搜索与PC连接的指定设备,然后开启线程循环等待设备回复
(2)现象:PC软件发送UDP广播包后,并没有收到设备的回复
(3)原因排查及定位:
经过排查跟本地的虚拟网卡有关,将所有虚拟网卡和其他网卡禁用以后就能收到设备的回复了
通过wireshark进一步抓包发现,在开启虚拟网卡时广播包是通过虚拟网卡发出的,并没有通过与设备连接的那个网卡发送出去,从而导致了广播包有去无回
找到原因那接下来的事情就好办了,但我们肯定不能要求每个用户都手动去禁用自己电脑上的其他网卡,这非常影响用户体验
那么最好的解决方案是:遍历本地的所有网卡,从每个网卡都发送广播包出去,以下为代码实现
// 绑定网卡IP发送广播包 bool SendBroadcast(char* ip) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); // 初始化SOCKET SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0); /* 本地端口和地址 */ SOCKADDR_IN src_addr; memset(&src_addr, 0, sizeof(src_addr)); src_addr.sin_family = AF_INET; // 指代协议族,在socket编程中只能是AF_INET src_addr.sin_port = 0; // 为0表示由系统自动分配端口 src_addr.sin_addr.s_addr = inet_addr(ip); int ret = bind(sockfd, (SOCKADDR *)&src_addr, sizeof(SOCKADDR)); if (SOCKET_ERROR == ret) { // 成功返回0,失败返回-1 return false; } bool opt = true; ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char*)&opt, sizeof(opt)); // 设置套接字 if (SOCKET_ERROR == ret) { // 成功返回0,失败返回-1 return false; } /* 目的端口和地址 */ SOCKADDR_IN dst_addr; memset(&dst_addr, 0, sizeof(dst_addr)); dst_addr.sin_family = AF_INET; dst_addr.sin_port = htons(5555); dst_addr.sin_addr.s_addr = INADDR_BROADCAST; // 广播地址 unsigned int buf[16] = { 0 }; buf[0] = 0x12345678; buf[1] = 0x87654321; ret = sendto(sockfd, (char*)buf, 64, 0, (sockaddr*)&dst_addr, sizeof(dst_addr)); if (SOCKET_ERROR == ret) { // 成功返回发送的字节数,失败返回-1 return false; } return true; } int main() { PIP_ADAPTER_INFO pAdapterInfo = NULL; ULONG ulSizeAdapterInfo = 0; DWORD dwStatus; dwStatus = GetAdaptersInfo(pAdapterInfo, &ulSizeAdapterInfo); if (dwStatus == ERROR_BUFFER_OVERFLOW) { if (!(pAdapterInfo = (PIP_ADAPTER_INFO)malloc(ulSizeAdapterInfo))) return; dwStatus = GetAdaptersInfo(pAdapterInfo, &ulSizeAdapterInfo); } if (dwStatus != ERROR_SUCCESS) return; while (pAdapterInfo) { PIP_ADDR_STRING ipList = &pAdapterInfo->IpAddressList; while (ipList) // 一个网卡可能有多个IP地址 { SendBroadcast(ipList->IpAddress.String); ipList = ipList->Next; }
pAdapterInfo = pAdapterInfo->Next; } return 0; }