Udp多播

讲解多播之前我们首先要走出TCP/UDP的一个误区:CS模型。事实上UDP的组播类似于一个消息订阅和发布中心,如果把组播当做一个发布订阅模型来理解的话,那么组播ip+端口就是消息中心。
订阅:客户端加入到当前组播,就类似于在消息中心订阅了数据包,凡是有数据来都会通知到客户端(Note:udp是不可靠的,不能保证数据包的顺序性和可达性)。
取消订阅:客户端退出当前组播就类似于取消订阅不在接收该组的数据包了。
发布消息:通过UDP的客户端像该组发送数据,当该组接收到该数据包的时候就会通知组内的所有成员。
所以通过上述比喻我们基本上可以得出UDP组播,并不存在客户端和服务器的说法,任何UDP连接都可以发送和接收当前消息。
多播详细,请参TCP/IP详解

下面我们将实现一个基本的UDP组播Demo
实现UDP的基本步骤:
1.创建UDP套接字
2.将当前套接字绑定到指定的组播端口(Note:绑定地址是本机地址
3.加入多播组
4.接收多播地址消息或者发布多播地址消息(Note:1.自己发送的数据包自己也会接收到,因为自己也加入到了群组。2.如果仅仅只是发送消息,可以不加入多播组,只需要直接把消息投递到多播组就行了
5.使用完了之后关闭套接字,清理资源

1.UDP获取组播消息,获取的时候必须加入到多播组

// UdpMutiCastRecv.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;

//测试组播地址:234.2.2.2:8888
// sockaddr_in dstAddr;
// dstAddr.sin_family = AF_INET;
// dstAddr.sin_port = htons(8888);
// inet_pton(AF_INET, "234.2.2.2", &dstAddr.sin_addr);//组播地址

int _tmain(int argc, _TCHAR* argv[])
{
    //初始化套接字
    WSADATA ws = {0};
    if (WSAStartup(MAKEWORD(2, 2), &ws) != 0)
    {
        perror("WSAStartup failed");
        WSACleanup();
        return 0;
    }
    //创建UDP套接字
    SOCKET mutcast_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (mutcast_sock == INVALID_SOCKET)
    {
        perror("socket server failed");
        WSACleanup();
        return 0;
    }
    //复用地址,允许同一个应用启动多个实例
    const int on = 1;
    setsockopt(mutcast_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
    //设置传输协议、端口以及目的地址 
    sockaddr_in mutcast_addr;
    mutcast_addr.sin_family = AF_INET;
    mutcast_addr.sin_port = htons(8888);//端口为组播端口
    mutcast_addr.sin_addr.S_un.S_addr = INADDR_ANY;//本机地址
    //将socket绑定地址
    if (bind(mutcast_sock, (sockaddr*)&mutcast_addr, sizeof(mutcast_addr)) == SOCKET_ERROR)     
    {
        perror("bind failed");
        closesocket(mutcast_sock);
        WSACleanup();
        return 0;
    }
    //加入组播
    ip_mreq multiCast;
    multiCast.imr_interface.S_un.S_addr = INADDR_ANY; //本地IP地址。
    inet_pton(AF_INET, "234.2.2.2", &multiCast.imr_multiaddr);//组播地址
    setsockopt(mutcast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multiCast, sizeof(multiCast));//加入组播地址

    //用于解析数据包源头的客户端的地址信息
    sockaddr_in client_addr = {0};
    int nclient_addr_len = sizeof(client_addr);
    char buff[1024];    //建立接收缓存字节数组 
    //循环接收组播数据包
    cout << "wait muticast package..." << endl;
    while (true)
    {
        memset(buff, 0, 1024);
        //开始接收数据 
        int len = recvfrom(mutcast_sock, buff, 1024, 0, (sockaddr*)&client_addr, &nclient_addr_len);
        if (len > 0)
        {
            char szBuf[255] = "";
            inet_ntop(AF_INET, &client_addr.sin_addr, szBuf, 255);
            cout << "客户端地址:" << szBuf << endl;
            cout << buff<<endl;
            //如果需要发送到指定的客户端,可以填充sendto的时候填充客户端地址,一般不会这么做
            //sendto(mutcast_sock, buff, lstrlenA(buff) + 1, 0, (sockaddr*)&client_addr, nclient_addr_len);
        }
    }

    closesocket(mutcast_sock);//关闭socket 
    WSACleanup();
    return 0;
}

2.UDP仅仅发送播组消息
1.创建UDP套接字
2.发送数据报
3.使用完了之后关闭套接字,清理资源

// UdpMutiCastSend.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <time.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    //初始化套接字
    WSADATA WSAData;
    WORD sockVersion = MAKEWORD(2, 2);
    if (WSAStartup(sockVersion, &WSAData) != 0)
        return 0;
    //创建UDP套接字
    SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (INVALID_SOCKET == clientSocket)
    {
        cout << "socket error!";
        return 0;
    }
    //组播地址
    sockaddr_in dstAddr;
    dstAddr.sin_family = AF_INET;
    dstAddr.sin_port = htons(8888);
    inet_pton(AF_INET, "234.2.2.2", &dstAddr.sin_addr);

    //发送数据
    srand(time(nullptr));
    int nflag = rand();
    char szbuf[1024] = "";
    sprintf_s<1024>(szbuf, "hellow word %d", nflag);
    sendto(clientSocket, szbuf, strlen(szbuf), 0, (sockaddr*)&dstAddr, sizeof(dstAddr));

    closesocket(clientSocket);
    WSACleanup();
    return 0;
}

测试结果:启动多个UdpMutiCastRecv程序,然后启动UdpMutiCastSend程序,运行结果如下:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/CAir2/article/details/80194949
今日推荐