windows C++ 通過UDP廣播獲取網路中所有裝置ip地址
阿新 • • 發佈:2019-01-28
說明:
原始碼使用說明,先在需要獲取IP地址的主機上執行server端程式,然後在需要搜尋主機的Pc上執行client端程式
本文是windows版,VC++,在VS2010環境下除錯成功。有時候需要搜尋網路中的裝置,機器,伺服器等,這就要要用到UDP廣播的方式,傳送廣播命令,廣播給網路中的每一個主機,該主機或裝置接收到廣播命令後,立刻傳送給請求端自己的裝置資訊,這裡以IP資訊為例子。
思路
在每一個裝置中部署sever端程式進行監控,client端傳送廣播命令,每一個server收到命令後,返回給client自己的ip地址資訊,給出程式碼如下,其中,有部分,比如獲取ip地址參考來自網路,尊重原創,樂於分享。
server端(windows控制檯程式)
server端監聽廣播命令”GetIPAddr”,收到命令後就相應
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
#define GET_HOST_COMMAND "GetIPAddr"
const int MAX_BUF_LEN = 255;
#define SERVER_PORT 12811
//只返回一個ip地址
bool GetLocalIP(char* ip)
{
//1.初始化wsa
WSADATA wsaData;
int ret=WSAStartup(MAKEWORD(2,2),&wsaData);
if (ret!=0)
{
return false;
}
//2.獲取主機名
char hostname[256];
ret=gethostname(hostname,sizeof(hostname));
if (ret==SOCKET_ERROR)
{
return false;
}
//3.獲取主機ip
HOSTENT* host=gethostbyname(hostname);
if (host==NULL)
{
return false;
}
//4.轉化為char*並拷貝返回
strcpy(ip,inet_ntoa(*(in_addr*)*host->h_addr_list));
return true;
}
bool doServer(){
int m_nPort = SERVER_PORT;
SOCKET sClient;
sockaddr_in clientAddr,bindAddr;
WSADATA wsdata;
//啟動SOCKET庫,版本為2.0
WORD wVer=MAKEWORD(2,0);
if( 0 != WSAStartup(wVer,&wsdata) )
{
//AfxMessageBox(L"Not Support Socket2.0");
return false;
}
//用UDP初始化套接字
sClient=socket(AF_INET,SOCK_DGRAM,0);
//設定該套接字為廣播型別,
BOOL optval=TRUE;
bindAddr.sin_family=AF_INET;
bindAddr.sin_addr.s_addr=htonl(INADDR_ANY);
bindAddr.sin_port=htons(m_nPort);
setsockopt(sClient,SOL_SOCKET,SO_BROADCAST,(char FAR *)&optval,sizeof(optval));
bind(sClient,(sockaddr *)&bindAddr,sizeof(sockaddr_in));
int nAddrLen = sizeof(SOCKADDR);
char buf[256] = {0};
int fromlength=sizeof(SOCKADDR);
printf("the server is start.\n");
char ipaddr[30] = {0};
char buff[MAX_BUF_LEN] = "";
if (GetLocalIP(ipaddr))
{
sprintf(buff, "my ip is:%s", ipaddr);
}
else
{
sprintf(buff, "%s", "my ip is:******");
}
//有多個ip地址的時候,這樣呼叫
//IPInfo ips[10];
//int len1 = 0;
//GetLocalIPs(ips, 10,&len1);
while(true)
{
int nRet = recvfrom(sClient,buf,256,0,(struct sockaddr FAR *)&clientAddr,(int FAR *)&fromlength);
if( SOCKET_ERROR != nRet )
{
char *pIPAddr = inet_ntoa(clientAddr.sin_addr);
if( NULL != pIPAddr )
{
WCHAR wzIPBuffer[32] = {0};
printf("clientAddr: %s\n", pIPAddr);
printf("receive command: %s\n", buf);
}
if (strcmp(buf,GET_HOST_COMMAND) != 0)
{
printf("the command not valid and was ignored.\n", buf);
continue;
}
// 傳送資料
int nSendSize = sendto(sClient, buff, strlen(buff), 0, (SOCKADDR*)&clientAddr, nAddrLen);
if(SOCKET_ERROR == nSendSize)
{
int err = WSAGetLastError();
printf("\"sendto\" error!, error code is %d\n", err);
return false;
}
}
else
{
//AfxMessageBox(L"Recv UDP Failed");
}
Sleep(1000);
}
closesocket(sClient);
return true;
}
int main()
{
if (!doServer())
{
printf("sever returned an error");
return -1;
}
return 0;
}
注意上面是隻有一個Ip地址的情況,有的機器可能會有多個IP地址,不同的網路,有線網,無線wifi等,需要獲取多個ip地址的方法:
//結構體記錄ip資訊
typedef struct tagIPInfo
{
char ip[30];
}IPInfo;
//獲取多個ip地址資訊列表
bool GetLocalIPs(IPInfo* ips,int maxCnt,int* cnt)
{
//1.初始化wsa
WSADATA wsaData;
int ret=WSAStartup(MAKEWORD(2,2),&wsaData);
if (ret!=0)
{
return false;
}
//2.獲取主機名
char hostname[256];
ret=gethostname(hostname,sizeof(hostname));
if (ret==SOCKET_ERROR)
{
return false;
}
//3.獲取主機ip
HOSTENT* host=gethostbyname(hostname);
if (host==NULL)
{
return false;
}
//4.逐個轉化為char*並拷貝返回
*cnt=host->h_length<maxCnt?host->h_length:maxCnt;
for (int i=0;i<*cnt;i++)
{
in_addr* addr=(in_addr*)*host->h_addr_list;
strcpy(ips[i].ip,inet_ntoa(addr[i]));
}
return true;
}
client端(windows控制檯程式)
client端傳送”GetIPAddr”命令,並及時接收client端發過來的資訊
//#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
const int MAX_BUF_LEN = 255;
#define GET_HOST_COMMAND "GetIPAddr"
#define CLIENT_PORT 11121
#define SERVER_PORT 12811
int main()
{
int nPort = SERVER_PORT;
WORD wVersionRequested;
WSADATA wsaData;
int err;
// 啟動socket api
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return -1;
}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
{
WSACleanup( );
return -1;
}
// 建立socket
SOCKET connect_socket;
connect_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(INVALID_SOCKET == connect_socket)
{
err = WSAGetLastError();
printf("\"socket\" error! error code is %d\n", err);
return -1;
}
// 用來繫結套接字
SOCKADDR_IN sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(CLIENT_PORT);
sin.sin_addr.s_addr = 0;
// 用來從網路上的廣播地址接收資料
SOCKADDR_IN sin_from;
sin_from.sin_family = AF_INET;
sin_from.sin_port = htons(nPort);
sin_from.sin_addr.s_addr = INADDR_BROADCAST;
//設定該套接字為廣播型別,
bool bOpt = true;
setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));
// 繫結套接字
err = bind(connect_socket, (SOCKADDR*)&sin, sizeof(SOCKADDR));
if(SOCKET_ERROR == err)
{
err = WSAGetLastError();
printf("\"bind\" error! error code is %d\n", err);
return -1;
}
printf("the client is start.\n");
int nAddrLen = sizeof(SOCKADDR);
char buff[MAX_BUF_LEN] = "";
int nLoop = 0;
char szMsg[]=GET_HOST_COMMAND;
int nLen=sizeof(sin_from);
if( SOCKET_ERROR==sendto(connect_socket, szMsg, strlen(szMsg), 0, (sockaddr*)&sin_from, nLen) )
{
// AfxMessageBox(L"Send UDP Failed");
return -1;
}
printf("send broadcast data:%s\n", GET_HOST_COMMAND);
while(true)
{
// 接收資料
int nSendSize = recvfrom(connect_socket, buff, MAX_BUF_LEN, 0, (SOCKADDR*)&sin_from, &nAddrLen);
if(SOCKET_ERROR == nSendSize)
{
err = WSAGetLastError();
printf("\"recvfrom\" error! error code is %d\n", err);
return -1;
}
buff[nSendSize] = '\0';
printf("received ip: %s\n", buff);
}
return 0;
}
執行結果
- 1.Server端
- 2.Client端
執行結果說明:兩個ip地址一樣,是因為client和server我都執行在同一臺機器上,如果有多臺server,則client可以搜素到多個ip,受到條件限制,這裡只有一個。