背景

最近在做网络摄像头(IPC、IPCam)方面的集成开发工作。因为项目的需求,要对接不同厂商的网络摄像头、安防摄像头等产品。
其中会涉及到一个ONVIF协议,这是个Web Service协议,其中一个接口就是Web Service Discover,也就是发现和侦测网络中有哪些可用的网络服务。
简单点说,就是往网络里面发送一个广播或者组播信息,然后其它设备或者服务器收到之后就返回一个响应,把服务接口信息反馈回来。

一、问题和现象

但是在开发测试的时候发现一个问题,发出去的组播信息无法被设备接收到,设备发出来的组播信息也无法被接收到。但是用另外一个官方的设备检测工具,可以探测出网络设备。

二、原因分析

  1. 因为别人家的软件能用,自己做的不能用。所以第一种想到的可能是,发送的信息不正确,导致摄像头拒绝返回信息。
    • 测试方法:本人用wireshark抓包,把能够正常探测服务和设备的那个软件发出去的所有信息都捕获出来,发现跟本人发送的内容一样的。
    • 结论:发送的数据没问题
  2. 第二种可能,就是发送的探测信息没有发送到相应的设备和服务那里,对方收不到自然就不会返回信息了。
    • 测试方法:在自己的代码里面开两个线程,这两个线程同时加入到一个广播组里面。一个发送一个接收。
    • 结论:线程A发送出去的信息,能被线程B接收,证明是有发出去的。
  3. 那就剩下一种可能了,就是信息已经在程序中发出去了,但是在网络中并没有传达到其它设备那里。
    • 因为别人家的设备可以发送,那就首先排除了是系统防火墙、杀毒软件、路由器、交换机等原因。问题应该是在本机上。
    • 然后想到本机上有两个网卡:一个有线网卡连了去摄像机的局域网,用来开发测试;一个无线网卡连了wifi,用来搜索文献资料。
    • 然后,就是google查找多网卡的时候,用socket编程进行广播、组播的注意事项。果然发现有资料提到过这个问题。原因简单点说,就是组播的信息如果没有指定用哪个网卡或者ip地址发出去的话,系统会选择一个默认的网卡。
    • 问题既然找到了,那就很好办了。

三、解决办法

  1. 读取本机所有网卡列表,以及每个网卡绑定的ip地址列表;
  2. 发送数据的时候,要指定某个网卡或者ip地址。
  3. 不同的平台、不同的开发环境框架之类的可能有所不同,需要根据平台环境来解决。

四、代码

因为本人做的平台集成涉及到 Windows、Linux、嵌入式Linux、MacOS、Android、iOS等多个平台,代码就不一一举例了(主要是还没来得及修bug)。



因为刚刚处理完windows平台上的,这里先给一个 C#的例子的,其他平台的代码上google找一下其实很多的。知道了问题所在,其实就没啥问题了。

int defaultPort = 3702;
string localName = Dns.GetHostName();
IPHostEntry hostEntry = new IPHostEntry();
hostEntry = Dns.GetHostByName(localName);
foreach (IPAddress ip in hostEntry.AddressList)
{
    //这里可以查看所有的ip地址
    Console.WriteLine(ip.ToString());
}
//本人只用了第0个ip地址来发送信息,如果不确定哪个ip地址的话,又或者想用多个IP地址来发送组播
//可以遍历一下AddressList,每个IP地址都发送一次。
IPAddress localAddress = hostEntry.AddressList[0];
Socket mcastSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//绑定IP地址和端口.
mcastSocket.Bind(new IPEndPoint(localAddress, defaultPort));
int optionValue = (int)IPAddress.HostToNetworkOrder(0);
IPAddress mcastAddress = IPAddress.Parse("239.255.255.250");
int mcastPort = 3702;
MulticastOption mcastOpt = new MulticastOption(mcastAddress, localAddress);
mcastSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, mcastOpt);
mcastSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, optionValue);
////发送广播包
byte[] bytes0 = Encoding.ASCII.GetBytes("本人用到的一大串字符串");
mcastSocket.SendTo(bytes0, new IPEndPoint(mcastAddress, mcastPort));

后记

有任何的疑问或者想法,或者对本文有质疑或者补充的话,欢迎在留言区评论,期待你的分享!




同时,也欢迎关注本人微信公众号或者打赏一下这篇文章。

参考文章
《如何︰ 使用 Visual Studio.NET 中的 MulticastInterface OptionName SetSocketOption()》





http://xzh.i3geek.com

爱唠叨的老鱼

爱唠叨的老鱼

技术经理,个人站长,创业者

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据