需求:设备的MAC地址出厂时已经固话号,IP地址可以由客户随便设置。相机一接上网线,无论其IP设置为多少,都可以通过上位机软件通过局域网将其搜索出来。
先来介绍几个概念:
广播域
- 广播域是网络中能接收任一台主机发出的广播帧的所有主机集合。也就是说,如果广播域内的其中一台主机发出一个广播帧,同一广播域(局域网)内所有的其它主机都可以收到该广播帧。
广播域的计算
- 如何知道一台主机是属于哪一个广播域呢?其实计算很简单,只要用主机的IP地址与子网掩码进行与运算即可知道该主机属于哪一个广播域。
- 例如:一台主机的IP地址为192.168.23.150,子网掩码为255.255.255.0,那么它所属的广播域就是192.168.23.150&255.255.255.0=192.168.23.0。那么其它的在广播域192.168.23.0内的所有主机就可以到该设备发送的广播包。如果把子网掩码改为255.255.0.0,那么它所属的广播域就是192.168.23.150&255.255.0.0=192.168.0.0。那么其它的在广播域192.168.0.0内的所有主机都可以收到该设备发送的广播包。
广播地址的计算
- 要想相同广播域内的其它主机能收到的广播帧,还需要在发送广播包的时候指定当前所属广播域内的广播地址。广播地址的计算方法为子网掩码取反再与广播域进行或运算。
- 例如:如果主机当前所属广播域为192.168.0.0,子网掩码为255.255.0.0,那么广播地址则为192.168.255.255。
- ~(255.255.0.0) | 192.168.0.0 = 0.0.255.255 | 192.168.0.0 = 192.168.255.255
使用UDP进行跨网段广播
- 要使主机A发送的广播包能够被另一网段的主机B收到,那么只需要更改主机A的子网掩码使得与主机B在同一个广播域内,再使用新的广播域的广播地址发送广播包即可。
- 例如:要使用192.168.23.150发送广播包让192.168.27.135收到,只需要设置192.168.23.150的子网掩码为255.255.0.0,然后再使用广播地址192.168.255.255即可。
特别要指出的是:255.255.255.255是受限广播地址,不能使用该地址发送广播包
方式一
- 采用 p2p方式,编写一个服务端程序和客户端程序,客户端程序自动向服务端报告本机IP和mac地址,由服务端进行服务调度,实现局域网发现;
方式二
- 采用arp协议,对局域网内活动的主机进行发现,这种方式主要是通过对局域网内的arp捕获,分析处理arp包的内容进行判断,实现局域网发现;
方式三
- 采用组播的方式,客户端所有主机都自动加入到组播中,在组播中,凡是存在的IP都是活跃折,实现局域网发现。
实际情况:
- 采用第二种方案也是有着局限性,因为arp协议是采用广播的方式进行,在没有外面的情况下,不同网段需要路由器转发,但路由器默认过滤广播包。所有,只有最终路由器才知道目的IP的MAC地址,如果进行跨网段通信的话,需要特定支持跨网段通信的路由器。
下面详细来说下udp组播方式如何实现的
- 组播(Multicast)传输:在发送者和每一接收者之间实现点对多点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包。它提高了数据传送效率。减少了骨干网络出现拥塞的可能性。
下面附上boost.asio的组播(Multicast)实现:
http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp03/multicast/sender.cpp http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp03/multicast/receiver.cpp
- client.cpp
#include
#include
#include
#include
/*
* 结论
* socket默认不支持发送广播报文,通过SO_BROADCAST选项的设置,开启广播发送功能。简单总结一下广播报文收发的规律:
* 1. 客户端socket开启SO_BROADCAST选项后才能发送广播to:255.255.255.255报文,否则调用sendto发送失败,会报错;
* 2. 服务端无需开启SO_BROADCAST,就可以接受广播数据包,SO_BROADCAST只是针对发送端的设置;
* 3. 服务端bind单播地址时,只接收目的地址为单播地址的报文,广播数据包不接收;
* 4. 服务端bind广播地址时,只接收客户端的广播数据,单播数据包不接收;
* 5. 服务端bind通用地址INADDR_ANY时,既能够接收客户端的单播报文,也能接收广播报文;
*/
int main(int argc, char **argv)
{
int sockfd, n, opt;
char recvline[256], sendbuf[256];
struct sockaddr_in servaddr, cliaddr;
//*****@gp_developer_server:~/test_code/linux系统udp单播广播$ ./udp_client 255.255.255.255
if (argc != 2)
{
printf("usage: argc != 2 %s n", argv[0]);
return 0;
}
printf("argv[0] = [%s]n", argv[0]);
// 创建socket
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0))
- server.cpp文件
#include
#include
#include
#include
int main(int argc, char **argv)
{
int sockfd, opt, n;
struct sockaddr_in servaddr;
char buff[256];
socklen_t cliaddrlen = sizeof(struct sockaddr_in);
// 创建socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// 绑定本地ip和端口
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
char* ch = argv[1];
switch (*ch)
{
case 'a':
{
// 设置任意地址
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
}
break;
case 'b':
{
// 设置固定ip地址
inet_pton(AF_INET, "192.168.50.10", &(servaddr.sin_addr));
}
break;
case 'c':
{
// 设置广播地址
inet_pton(AF_INET, "255.255.255.255", &(servaddr.sin_addr));
}
break;
default:
{
printf("agrv is error ch=[%s]", ch);
}
break;
}
// 绑定端口
servaddr.sin_port = htons(10000);
if ((n = bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net