广播、组播、单播区别与联系

   日期:2021-02-01     浏览:119    评论:0    
核心提示:1.引言网络通信中使用最多的就是广播、组播、单播几种通信方式了,今天我们抛开具体的标准和知识,简单聊聊单播、组播、广播的区别与使用。2.单播、组播、广播区别与联系单播:在同一网络内,两个设备点对点的通信就是单播通信。组播:在同一网络可达范围内,一个网络设备与关心其数据的部分设备进行通信就是组播。广播:在同一网络可达范围内,一个网络设备向本网络内所有设备进行通信就是广播。具体如下:简单地说,单播->组播->广播,是通信数量不断增加的通信方式。当然,通信数量的增多,带

1.引言

网络通信中使用最多的就是广播、组播、单播几种通信方式了,今天我们抛开具体的标准和知识,简单聊聊单播、组播、广播的区别与使用。

2.单播、组播、广播区别与联系

单播:在同一网络内,两个设备点对点的通信就是单播通信。

组播:在同一网络可达范围内,一个网络设备与关心其数据的部分设备进行通信就是组播。

广播:在同一网络可达范围内,一个网络设备向本网络内所有设备进行通信就是广播。

具体如下:

简单地说,单播->组播->广播,是通信数量不断增加的通信方式。当然,通信数量的增多,带来的是通信设备的资源消耗更大,整体网络环境的复杂度更高。

通常,我们使用组播、广播完成两件事:

1)将同一份数据交互到多个目的地。比如,视频会议、新闻分发,都需要将一份数据同时传输到多个设备上,供大家使用。

2)通过客户端请求或发现服务器。有时,我们并不知道服务器的具体信息(如IP地址),这时,我们可以采取“盲发”的方式去广播或组播信息,等待服务器收到消息盲发的消息后,返回数据,如此找到对应目标设备。

众所周知,TCP是可靠传输(先与另一个通信端点建立可靠连接,再传输数据),因此TCP一般只支持单播这种通信方式,而DUP通信不需要建立连接就可以发送数据,因此,通常我们说的广播、组播,都是在UDP下概念。

此外,广播又可以分为两类:本地广播、定向广播。

1)本地广播:广播地址为255.255.255.255.

2)定向广播:广播地址类似192.168.4.255.

这两种广播功能类似,但具体区别说来话长,有感兴趣的可以留言,我再出篇帖子来介绍一下。

3.编程与测试

广播、单播在实现方式,以及使用方式上的区别不大,仅仅是目标IP,以及Socket属性的细微差别,我们先来看两者的区别与使用。

具体可参考以下代码:(开发板仍然是便宜且好用的ESP32开发板,开发环境是release V4.2,实际上,任何平台,以下代码都是可以参考滴,客官慢用)


#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "tcpip_adapter.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "addr_from_stdin.h"

#if defined(CONFIG_EXAMPLE_IPV4)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#elif defined(CONFIG_EXAMPLE_IPV6)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#else
#define HOST_IP_ADDR ""
#endif

#define PORT CONFIG_EXAMPLE_PORT

static const char *TAG = "example";
static const char *payload = "Message from ESP32 ";

static int udp_creat(uint16_t port, char* bind_ip)
{
    struct sockaddr_in6 unicast_dest_addr = {0};
    struct sockaddr_in *unicast_dest_addr_ip4 = (struct sockaddr_in *)&unicast_dest_addr;
    int ip_protocol = 0;
    const int on = 1;
    inet_aton(bind_ip, &unicast_dest_addr_ip4->sin_addr.s_addr);//bind sta ip
    unicast_dest_addr_ip4->sin_family = AF_INET;
    unicast_dest_addr_ip4->sin_port = htons(port);
    ip_protocol = IPPROTO_IP;

    int sock = socket(AF_INET, SOCK_DGRAM, ip_protocol);
    if (sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        return -1;
    }

    int err = bind(sock, (struct sockaddr *)&unicast_dest_addr, sizeof(unicast_dest_addr));
    if (err < 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        shutdown(sock, 0);
        close(sock);
        return -1;
    }
    
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) {
        ESP_LOGE(TAG, "reuse addr fail");
        close(sock);
        return -1;
    }

    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) != 0) {
        ESP_LOGE(TAG, "broadcast enable fail");
        close(sock);
        return -1;
    }

    return sock;
}

static void udp_client_task(void *pvParameters)
{
    char rx_buffer[128];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;
    tcpip_adapter_ip_info_t sta_info;
    while (1) {

#if defined(CONFIG_EXAMPLE_IPV4)
        struct sockaddr_in unicast_dest_addr;
        unicast_dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
        unicast_dest_addr.sin_family = AF_INET;
        unicast_dest_addr.sin_port = htons(PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;
#elif defined(CONFIG_EXAMPLE_IPV6)
        struct sockaddr_in6 unicast_dest_addr = { 0 };
        inet6_aton(HOST_IP_ADDR, &unicast_dest_addr.sin6_addr);
        unicast_dest_addr.sin6_family = AF_INET6;
        unicast_dest_addr.sin6_port = htons(PORT);
        unicast_dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
        addr_family = AF_INET6;
        ip_protocol = IPPROTO_IPV6;
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
        struct sockaddr_in6 unicast_dest_addr = { 0 };
        ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_DGRAM, &ip_protocol, &addr_family, &unicast_dest_addr));
#endif
        char sta_ip_str[32] = {0};
        esp_err_t ret = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &sta_info);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "get sta ip fail");
        }
        printf(IPSTR, IP2STR(&sta_info.ip));
        sprintf(sta_ip_str, IPSTR, IP2STR(&sta_info.ip));
        int sock = udp_creat(3333,sta_ip_str);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }

        ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, PORT);
        struct sockaddr_in broadcast_dest_addr;//for broadcast
        char sendline[32] = {"Hello"};
        char temp_str[32] = "255.255.255.255";
        bzero(&broadcast_dest_addr, sizeof(broadcast_dest_addr));
        broadcast_dest_addr.sin_family = AF_INET;
        broadcast_dest_addr.sin_addr.s_addr = inet_addr(temp_str); //广播地址(192.168.43.255 can be work, too.)
        broadcast_dest_addr.sin_port = htons(3333);//target port

        while (1) {
            int err = sendto(sock, sendline, strlen(sendline), 0, (struct sockaddr*)&broadcast_dest_addr, sizeof(broadcast_dest_addr));//broadcast
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
            ESP_LOGI(TAG, "Message sent");

            err = sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)&unicast_dest_addr, sizeof(unicast_dest_addr));//unicast
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
            ESP_LOGI(TAG, "Message sent");

            struct sockaddr_in source_addr; // Large enough for both IPv4 or IPv6
            socklen_t socklen = sizeof(source_addr);
            int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);

            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
                break;
            }
            // Data received
            else {
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                ESP_LOGI(TAG, "Received %s", rx_buffer);
                if (strncmp(rx_buffer, "OK: ", 4) == 0) {
                    ESP_LOGI(TAG, "Received expected message, reconnecting");
                    break;
                }
            }

            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    
    ESP_ERROR_CHECK(example_connect());

    xTaskCreate(udp_client_task, "udp_client", 4096, NULL, 5, NULL);
}

上述代码,通过UDP,实现广播、单播一次,然后进入接收一次数据;依次循环的功能。

4.组播实现介绍

组播的实现就略微复杂了,要实现组播,至少要经过以下步骤:

1)建立socket_fd

2)socket_fd和指定本地端口绑定

3)加入一个组播组

4)通过sendto / recvfrom进行数据的收发

5)离开组播组

6)关闭socket

注意:服务器和客户端必须都要加入相同的组播地址才可以。涉及到的socket属性主要是以下三个:

感兴趣的小伙伴可以留言,我将再出一篇关于组播的博客,谢谢点赞或收藏。

 

 

 

 

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服