最近在項目中遇到一個關於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; }
