1. 程式人生 > >Udp多播

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程式,執行結果如下:
這裡寫圖片描述