UDP组播的实现

os:花了一周时间实现了UDP协议下的组播程序,上面是网上给出的一般组播实现的架构。2~4是针对socketopt即socket属性的设置;TTL设置超时时间即每次发送数据之间的间隔,必须有,但不一定用soketopt实现,下文会细说。LOOP是设置回环,一开始不明白什么意思,之后看师兄做了测试,如果不开启回环则发送的机器将不会接收到自己发送的数据。(LOOP默认开启)ADD是加入多播组,这一步很关键,相当于把所加入的播组ip和本机ip绑定到一起。

一,我需要准备什么
        之前做过点对点的基于MFC的UDP和TCP程序,这次直接用API编写,有一些地方不太一样,首先是不能用MFC提供的socket组件alsocket了,直接用API编程必做的链接是
#include <WinSock2.h>
#include <iostream>
using namespace std;
#pragma  comment (lib,"wsock32.lib")
当然要想实现组播功能#include<ws2tcpip.h> 是必不可少的。下文会对它简单介绍。

二,我想要做什么
       用英语都写不出来的东西就别指望用代码写了。在写组播程序之前,需要先了解组播和之前的点对点通信的区别和相同点在哪里。 首先TCP协议是面向连接的,可以通过多次点对点通信实现组播,但这不是我们需要的。相比之下UDP协议提供了组播功能和相应的函数。
        通过之后的编写,我发现称UDP组播过程中双方只有接收方的socket绑定了端口。也就是说组播的发送端(所谓服务器)使用的是未命名端口,而接受端(所谓客户端)则需要绑定端口,这和之前的经验恰恰相反。于是我又重新查看了bind()函数的定义: bind()函数并不是总是需要调用的,只有用户进程想与一个具体的地址或端口相关联的时候才需要调用这个函数。如果用户进程没有这个需要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择,而不需要调用bind()函数,同时也避免不必要的复杂度。在一般情况下,对于服务器进程问题需要调用bind()函数,对于客户进程则不需要调用bind()函数。 从这段解释我们不难得到几个观点:1,sokcet必须要绑定己方地址才能工作,但是win提供了自动绑定机制,如果不需要知道己方socket的地址和端口,则无需手动bind。2,与在create时bind的CSocket不同,socket函数只能选择地址族而不绑定。3,是否bind与客户端还是服务器无关,而与发送或接收有关,如果一个程序只需要发送,那么无需手动bind交给系统分配就好,如果程序需要接受信息,那么必须bind固定端口和地址,以保证发送方可以正确寻址。
        经过上面的分析可以总结一下UDP组播程序的一般编写流程:
发送端:
            创建socket(选定地址族和协议版本等),设置socketopt为允许广播,设置socketopt中的IP_MULTICAST_TTL(需包含ws2tcpip头文件,或用Sleep函数实现),设置socketopt允许回环(默认允许,可省略),设置发往的组播ip和端口号,调用sendto()函数,关闭socket。
接收端:
          创建socket(选定地址族和协议版本等),绑定本地ip和端口号【注意:这一步很重要,此时接收端绑定的ip只要是本机ip就可以了,而不是发送端发往的ip,但是端口号一定要是发送端发往的端口号。】,    设置socketopt允许回环(默认允许,可省略),设置socketopt中的IP_MULTICAST_TTL(需包含ws2tcpip头文件,或用Sleep函数实现),设置socketopt,“绑定”要加入的组播地址和本机地址【此处不用再设置端口了,之前没有理解这一步的意义将本地ip写成了本地端口致错】,recvfrom接收【没错,from的地址不是组播地址也不是发送方地址而是本机地址】,退出播组,关闭socket。

三,我要怎么做
        直接用winAPI和win控制台来编写这个程序,网上linux的组播代码很多,基于win32的确不多。考虑到代码段不长,于是干脆自己写一个。
        按照上面的流程操作即可,需要注意的有两点。一个是setsockopt 函数中第四个参数需要强制类型转换我用的是reinterpret_cast<char FAR *> 实现。第二点比较坑,就是是会有一个warning导致编译不!通!过!
error C4996: 'inet_addr': Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
1> c:\program files (x86)\windows kits\8.1\include\um\winsock2.h(1850) : 参见“inet_addr”的声明
就是上面这个,用vs2013调用inet_addr 函数的话就会报错,微软希望你使用inet_pton这个新函数。要想用这个新函数需要包含一个新的头文件,而且用法和inet_addr也不大一样。网上给出了一个便捷的解决方法参考: http://jingyan.todgo.com/shuma/1567699qmh.html 这位老哥给出了关闭项目属性中sdl检查的方法,关闭之后编译通过。
四,代码

发送端
#include <WinSock2.h>
#include <iostream>

using namespace std;
#pragma  comment (lib,"wsock32.lib")

#define MCAST_PORT 8888
#define MCAST_ADDR "224.0.0.88"  // 多播地址
#define MCAST_DATA "BROADCAST TEST DATA"  // 多播内容
#define MCAST_INTERVAL 5000  //多播时间间隔
using namespace std;

void main()
 {
         SOCKET sock;
         struct sockaddr_in mcast_addr;
         WORD wVersionRequested = MAKEWORD(2,0);
         WSADATA wsaData;
         int err = WSAStartup(wVersionRequested, &wsaData);
         if (err != 0)
         {
             cout << err << endl;
             cout << "socket版本初始化失败" << endl;
         }
         if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0)

         {
             WSACleanup();
             return;
         }
         sock = socket(AF_INET, SOCK_DGRAM, 0);
         if (sock == INVALID_SOCKET)
                 {
                     cout << "socket创建失败" << endl;
                     cout << WSAGetLastError() << endl;
                     return;
                 }
            memset(&mcast_addr, 0, sizeof(mcast_addr));
            bool opt = 1;
            setsockopt(sock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char FAR *>(&opt), sizeof(opt));
            mcast_addr.sin_family = AF_INET;
            mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);  //inet_npton
            mcast_addr.sin_port = htons(MCAST_PORT);
             while (1)
                 {       //向局部多播地址发送多播内容
                        int n = sendto(sock, MCAST_DATA, sizeof(MCAST_DATA), 0, (struct sockaddr*)&mcast_addr, sizeof(mcast_addr));
                         if (n<0)
                             {
                                     cout << "send error" << endl;
                                     return ;
                             }
                         else
                             {
                                     cout << "send message is going ...." << endl;
                             }
                         Sleep(MCAST_INTERVAL);

                     }
             if (!closesocket(sock)) //关闭套接字
             {
                 WSAGetLastError();
                 return;
             }
             if (!WSACleanup())       //关闭Socket库
             {
                 WSAGetLastError();
                 return;
             }
     }
接收端
#include<iostream>
#include<WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
#include<ws2tcpip.h>
using namespace std;
#define MCAST_PORT 8888
#define MCAST_ADDR "224.0.0.88"
#define LOCAL_ADDR "172.31.171.89"
#define MCAST_INTERVAL 5000
#define MCAST_SIZE 1024

void main()
{
    SOCKET sock;
    WORD wVersionRequested = MAKEWORD(2,0);
    WSADATA wasData;
    sockaddr_in local_addr;
    int err = WSAStartup(wVersionRequested, &wasData);
    if (err != 0)
    {
        cout << err << endl;
        cout << "套接字版本错误" << endl;
        return;
    }
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(MCAST_PORT);
    local_addr.sin_addr.s_addr = inet_addr(LOCAL_ADDR);
    err = bind(sock, (struct sockaddr*)&local_addr, sizeof(local_addr));
    if (err < 0)
    {
        cout << "bind error" << endl;
        cout << err << endl;
        return;
    }
    bool loop = 1;
    err = setsockopt(sock,IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char FAR *>(&loop), sizeof(loop));
             if (err<0)
                 {
                         cout << "set sock error" << endl;
                         return ;
                 }
             struct ip_mreq mreq;
             mreq.imr_multiaddr.S_un.S_addr = inet_addr(MCAST_ADDR);
             mreq.imr_interface.s_addr = inet_addr(LOCAL_ADDR);
             err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char FAR *>(&mreq), sizeof(mreq));
             if (err< 0)
             {
                 cout << err << endl;
                 cout << "set sock error2" << endl;
                 return;
             }
             socklen_t addr_len = 0;
             int times = 0;
             char buff[MCAST_SIZE];
             int n = 0;
             for (times = 0;times <= 5; times++)
             {
                 addr_len = sizeof(local_addr);
                 memset(buff, 0, MCAST_SIZE);
                 n = recvfrom(sock, buff, MCAST_SIZE, 0, (struct sockaddr*)&local_addr, &addr_len);
                 if (n == -1)
                 {
                     cout << "cont recv" << endl;
                     times = 0;
                     return;
                 }
                 cout << times+1 <<" "<< buff << endl;
                 Sleep(MCAST_INTERVAL);
             }
             err = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, reinterpret_cast<char FAR *>(&mreq), sizeof(mreq));
             closesocket(sock);
             return;


}




### 回答1: UDP组播是一种在网络中传输数据的协议。它可以实现局域网内的IPC设备发现,即在局域网中通过UDP组播的方式来发现IPC设备。以下是实现这一过程的主要步骤。 首先,需要在局域网内配置一个多播组地址。多播组地址是一组预定义的IP地址,用于标识一个多播组。这个地址需要在局域网内唯一,可以通过网络管理员进行配置。 然后,IPC设备需要加入到这个多播组中。这个过程可以通过设备自身的操作或者通过网络管理员的配置来完成。设备加入多播组后,就可以接收到多播组内的消息。 接下来,需要有一个设备或者一个应用程序作为发送方。这个发送方通过UDP协议向多播组地址发送特定的发现消息。这个发现消息可以是一个特定的字符串,用来标识IPC设备。 当IPC设备收到发现消息时,会进行相应的处理。一般来说,设备会根据发现消息中的标识进行识别,如果是自己的标识,就会发送一个应答消息给发送方。 最后,发送方收到设备的应答消息后,就可以确定设备的存在,并进行相应的操作。例如,可以获取设备的IP地址、设备类型等信息,并在应用程序中显示或者进行其他操作。 总的来说,通过UDP组播可以实现局域网内的IPC设备发现。通过加入到预定义的多播组中,IPC设备可以接收到发现消息,在收到消息后发送应答消息,从而完成设备的发现。这种方式简化了设备发现的过程,提高了设备的可管理性和易用性。 ### 回答2: UDP组播是一种在局域网中实现IPC设备发现的通信方式。IPC设备是指网络摄像头、门禁系统等安防设备。组播是一种一对多的通信方式,即一个消息可以同时发送给多个接收者。 要实现局域网IPC设备发现,首先需要选择一个组播地址。组播地址是一个特殊的IP地址,用于向特定的组播组发送消息。然后,在局域网中的每个IPC设备上,都需要开启组播服务,并加入到指定的组播组中。 当一个设备想要发现其他IPC设备时,它会发送一个组播消息到组播地址。其他设备收到这个消息后,会回复一个确认消息。通过这种方式,可以实现设备之间的发现和通信。 为了确保消息的可靠传输,可以使用UDP协议来发送组播消息。UDP协议是一种简单的传输层协议,具有高效和快速的特点。通过UDP组播,可以减少通信的延迟时间和网络带宽的占用。 通过UDP组播实现局域网IPC设备发现,可以方便地管理和控制安防设备。管理员可以通过组播消息了解设备的存在和状态,并进行相应的配置调整。同时,设备之间也可以通过组播消息进行数据同步和共享。 总之,UDP组播是一种实现局域网IPC设备发现的有效通信方式。它可以帮助管理员轻松管理安防设备,并提升设备之间的协作效率。 ### 回答3: 在局域网中,为了实现IPC设备的发现和管理,可以使用UDP组播技术。UDP组播是一种将单个数据包发送给多个目标地址的通信方式。 首先,设定一个固定的组播IP地址和端口号,用于IPC设备的发现。在局域网中的每个IPC设备都会监听这个组播地址和端口号。 当一个控制中心希望发现局域网中的IPC设备,它会向组播IP地址和端口号发送一个广播消息。这个消息包含了控制中心的信息,如IP地址、端口号等。 每个IPC设备都会接收到这个广播消息并解析其中的内容。如果IPC设备符合控制中心的要求,它会发送一个响应消息给控制中心,包含了自己的信息,如IP地址、端口号、设备型号等。 控制中心接收到响应消息后,可以根据IPC设备的信息进行设备的管理和控制。 UDP组播能够实现局域网中IPC设备的发现,主要有以下优势: 1. 通过单次广播消息,可以同时发现多个IPC设备,提高设备发现的效率。 2. 组播方式使用UDP协议,具有较低的网络负载和资源消耗,适用于局域网中的设备发现。 3. 消息通讯采用无连接的方式,简化了设备的配置和管理过程。 通过UDP组播实现局域网IPC设备的发现,能够方便、高效地管理和控制局域网中的设备,提升了设备管理的便利性和操作效率。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值