一、说明

1.1 标准组播解释

通信分为单播、多播(即组播)、广播三种方式

单播指发送者发送之后,IP数据包被路由器发往目的IP指定的唯一一台设备的通信形式,比如你现在与web服务器通信就是单播形式

广播指发送者发送之后,IP数据包被路由器发给与其连接的所有设备的通信形式

组播指发送者发送之后,IP数据包被路由器发往目的IP对应组播组名下所有主机的通信形式

1.2 个人理解组播解释

对于标准的组播解释,说明似乎还算是清楚的,但具体到技术就有很多问题。比如我将数据包发往一个组播地址,这个组播地址对应一台物理设备吗?如果不是一台物理设备那谁依据什么向哪些主机发送该数据包等等。

结合各资料和自己测试的情况总结出了以下几点:

1) 编写发送程序:组播数据包是且只是目的IP是组播保留地址的UDP数据包,与正常UDP数据包的区别只是其目的IP是组播保留IP

2) 发送数据包主机:网卡在看到目的IP是组播保留IP后,自动将目的mac地址改成组播mac地址然后向其各端口都发送出去

3) 交换机:交换机在接收到数据包之后,通过目的mac地址认识到这是一个组播数据包,修改源mac为自己mac、保持目的mac为组播mac不变向其各端口都发送出去(交换机对组播包的处理和广播包应该是一样的,或者说对于交换机只有单播包和广播包)

4) 路由器:路由器在接收到数据包之后,通过目的mac地址或目的ip地址认识到这是一个组播数据包,修改源mac为自己mac、保持目的mac为组播mac不变,保持源IP不变、保持目的IP为组播保留IP不变,依照与路由表类似的“组播组地址表”向与目的IP匹配的一个或多端口将数据包发送出去

5) 接收数据包主机:接收数据包主机要想接收到发送主机发送的数据包,首先他要(向路由器说明)加入发送者发往的组播组,然后他要在本地启动一个进程监听发送者发往的端口

6) 组播需要硬件支持,有些路由器是不支持组播的,就直观感受看如果在全球实现组播那维护“组播组地址表”会给路由器带来很大负担路由大厂商应该也不是很愿意支持组播;也就是说理论上组播可以在广域网上实现,但其实一般只在局域网中(能够)使用。

7)  注意从本质上而言,接收组播数据包的主机只是启了一个UDP监听,他本身并不能识别是组播发过来的数据包还是直接发过来的数据包(除非对收到的数据包的目的IP是否为组播IP进行判断,但获取目的IP是件很麻烦的事)。也就是说该监听不只是可以接收组播数据包,任何其他如果主机直接向该监听的端口发送UDP数据包该主机也是可以接收到的(已确认过)。

假设使用组播地址为239.255.255.252,使用组播端口为23456,通信举例如下:

发送者S----发送者向239.255.255.252:23456发送一个UDP数据包Packet1

接收者R1(假设其IP地址为134.192.1.100)----第一步启动进程监听239.255.255.252:23456;第二步通过setsockopt加入组播组(239.255.255.252)

接收者R2(假设其IP地址为134.192.1.101)----第一步启动进程监听239.255.255.252:23456;第二步通过setsockopt加入组播组(239.255.255.252)

最终效果----发送者发往239.255.255.252:23456的udp数据包,R1的23456端口收到一份Packet1,R2的23456端口收到一份Packet1

1.3 谁是服务端引发的混乱

我在前面一直使用“发送者”、“接收者”,而没有使用“服务端”、“客户端”,因为“服务端”和“客户端”在组播中容易引发混乱。

在我们一般的socket编程中都是服务端去bind;但在组播中是反过来,客户端(接收者)去bind,而发送者(服务端)是不用去bind的(注意是不用而不是不能,你非要bind也是可以的,bind和不bind只是使用固定端口还是使用随机端口的区别)。

有些小伙伴意识到了这个问题,为了与习惯一致,所以直接将发送者称为客户端,接收者称为服务端。

其实这种叫法是不合适的,以初高中常见的电脑课场景为例:老师控制所有电脑显示老师的电脑操作,这时作为发送者的老师电脑从认知上确实应该是服务端而不是客户端才对。

如果你读了半天没听懂这里在说什么,那不必在意,记得组播中尽里使用“发送者”和“接收者”,少用“服务端”和“客户端”就对了。

1.4 监听组播地址与监听本地地址的区别讨论

在一般的关于组播的文章中,接收者设置的监听地址都是本地地址(如134.192.1.100:23456);但在前面1.2中我们要求接收者R1和R2监听的不是本地地址,而是组播地址239.255.255.252:23456。这是为什么呢?

首先说,这两种监听形式都可以接收到发往239.255.255.252:23456的组播数据包。他们之间的区别是,监听239.255.255.252:23456地址能且只能接收发往239.255.255.252:23456的数据包,而监听134.192.1.100:23456除了能接收发往239.255.255.252:23456的数据包外还能接收直接发往134.192.1.100:23456的数据包。

这样造成的安全区别是,假如接收者实现了这么一项功能:接收到一个打开telnet的命令就直接开启本机的telnet服务。如果监听的是239.255.255.252:23456那么攻击者只有通过局域网内主机发出组播数据包才能打开telnet,但如果监听的是134.192.1.100:23456那么攻击者可以远程直接向134.192.1.100:23456发送数据包开启telnet。

linux上可以通过SO_BINDTODEVICE选项绑定网卡然后设置监听组播地址,但Windows没有实现SO_BINDTODEVICE暂时也不知道如何实现类似功能,所以在下边代码中我也只是linux系统设置了监听组播地址其他系统仍只是监听本地地址。

二、程序实现

2.1 程序代码

发送者代码:

import time
import socket # 组播组IP和端口
mcast_group_ip = '239.255.255.252'
mcast_group_port = 23456 def sender():
# 建立发送socket,和正常UDP数据包没区别
send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 每十秒发送一遍消息
while True:
message = "this message send via mcast !"
# 发送写法和正常UDP数据包的还是完全没区别
# 猜测只可能是网卡自己在识别到目的ip是组播地址后,自动将目的mac地址设为多播mac地址
send_sock.sendto(message.encode(), (mcast_group_ip, mcast_group_port))
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: message send finish')
time.sleep(10) if __name__ == "__main__":
sender()

接收者代码:

import sys
import struct
import time
import socket # linux能绑定网卡这里绑定组播IP地址不会服错,windows没法绑定网卡这里不能绑定组播IP地址只能绑定本网卡IP地址
if "linux" in sys.platform:
# 绑定到的网卡名,如果自己的不是eth0则注意修改
nic_name = "eth0"
# 监听的组播地址
mcast_group_ip = "239.255.255.252"
else:
mcast_group_ip = socket.gethostbyname(socket.gethostname())
mcast_group_port = 23456 def receiver():
# 建立接收socket,和正常UDP数据包没区别
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 25是linux上的socket.SO_BINDTODEVICE的宏定义,但由于windows没实现SO_BINDTODEVICE,所以python索性也就没有实现SO_BINDTODEVICE,我们直接使用25
# windows没有实现SO_BINDTODEVICE,所以不能通过这种方式绑定网卡,windows怎么实现绑定网卡暂不清楚
if "linux" in sys.platform:
sock.setsockopt(socket.SOL_SOCKET, 25, nic_name)
# linux能绑定网卡这里绑定组播IP地址不会服错,windows没法绑定网卡这里不能绑定组播IP地址只能绑定本网卡IP地址
sock.bind((mcast_group_ip, mcast_group_port))
# 加入组播组
mreq = struct.pack("=4sl", socket.inet_aton(mcast_group_ip), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,mreq) # 允许端口复用,看到很多教程都有没想清楚意义是什么,我这里直接注释掉
# sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 设置非阻塞,看到很多教程都有也没想清楚有什么用,我这里直接注释掉
# sock.setblocking(0)
while True:
try:
message, addr = sock.recvfrom(1024)
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Receive data from {addr}: {message.decode()}')
except :
print("while receive message error occur") if __name__ == "__main__":
receiver()

2.2 运行截图

发送者截图:

接收者运行截图:

参考:

https://blog.csdn.net/ztb3214/article/details/19285363

https://blog.csdn.net/s_lisheng/article/details/74938534?locationNum=10&fps=1

http://blog.sina.com.cn/s/blog_69c81c3e0102x9pm.html

Python3组播通信编程实现教程(发送者+接收者)的更多相关文章

  1. IPv4组播通信原理

    2011-05-08 21:21:14 标签:组播 vin_do,vin_do学习笔记,笔记 休闲 职场 摘自网络,感谢原作者 摘要: 本文试图成为学习TCP/IP网络组播技术的入门材料.文中介绍了组 ...

  2. linux网络编程之一-----多播(组播)编程

    什么是多播 组播(Multicast)是网络一种点对多(one to many)的通信方式,通过报文复制完成网络中一台server对应多台接收者的高效数据传 送.对其形象的比喻就是类似于广播电台和电视 ...

  3. zigbee组播通信原理

    组播: 在zigbee网络里面,把网络节点标记为组的方式来进行通信:发送模块如果发送的组号和网络里标记模块的组号相对应,那么这些模块就可以拿到这些无线数据包. 特点: 1.分组中组的编号有两个字节. ...

  4. linux网络编程之二-----多播(组播)编程

    多播编程实例 服务器端 下面是一个多播服务器的例子.多播服务器的程序设计很简单,建立一个数据包套接字,选定多播的IP地址和端口,直接向此多播地址发送数据就可以了.多播服务器的程序设计,不需要服务器加入 ...

  5. IP组播技术介绍及实现例子

    引 言 近年来,随着Internet的迅速普及和爆炸性发展,在Internet上产生了许多新的应用,其中不少是高带宽的多媒体应用,譬如网 络视频会议.网络音频/视频广播.AOD/VOD.股市行情发布. ...

  6. Ztack学习笔记(6)-广播组播点播

    Zigbee网络中进行数据通信主要有三种类型:单播.组播.广播.那这三种方式如何设置呢,在哪里设置呢, 一. 广播 当应用程序需要将数据包发送给网络的每一个设备时,使用这种模式.广播的短地址有三种 0 ...

  7. IP组播

    1  IP组播基础 IP组播技术有效地解决了单点发送.多点接收的问题.组播源只发送一份数据,被传递的信息在距组播源尽可能远的网络节点才开始被复制和分发,并且只发送给需要该信息的接收者.  说明: 本章 ...

  8. IPSec组播概要

    IPSec作为主流IP安全协议之一,在单播环境下,特别是在VPN场景中应用广泛.但是在组播环境貌似看到的不多,通过RFC4301了解到IPSec首先是支持组播的,即通过手动配置的方式可以实现组播包加密 ...

  9. D类IP地址和组播传输

    在224.0.0.0-239.255.255.255范围内的地址称为D类IP组播地址.其中,224.0.0.0-224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配 ...

随机推荐

  1. Python3简单爬虫抓取网页图片

    现在网上有很多python2写的爬虫抓取网页图片的实例,但不适用新手(新手都使用python3环境,不兼容python2), 所以我用Python3的语法写了一个简单抓取网页图片的实例,希望能够帮助到 ...

  2. Python pycharm 常用快捷键

    快捷键 1.编辑(Editing) Ctrl + Space 基本的代码完成(类.方法.属性) Ctrl + Alt + Space 快速导入任意类 Ctrl + Shift + Enter 语句完成 ...

  3. Runnable、Callable、Executor、Future、FutureTask关系解读

    在再度温习Java5的并发编程的知识点时发现,首要的就是把Runnable.Callable.Executor.Future等的关系搞明白,遂有了下述小测试程序,通过这个例子上述三者的关系就一目了然了 ...

  4. .Net MVC关于子页面引用js库问题

    layout页面中的配置: @RenderSection("scripts", required: false) @RenderSection("Styles" ...

  5. 解決 Android Studio 不停 Indexing 的問題(Updating Indices: Indexing paused due to batch update)

    遇到這個問題通常是 IDE 更新後,或是反覆使用 Android Studio 開啟其他專案所導致,解決方法其實非常簡單喔! 点击 這個選項的功用是「清除 IDE 暫存並重啟」,沒錯,會出現上述情形的 ...

  6. hibernate框架模板(可复制修改)

    简易搭建jar包 User类 package com.littlepage.test; public class User { private int uid; private String unam ...

  7. EF延迟加载和懒加载

    EF默认是延迟加载的 延迟加载就是刚开始只会读取当前实体对应表的数据 关联表的数据不会读取 只有下面条件用到了才会再去读取 所以可能会造成N次读取数据库  需要在实体的属性加virtual关键字 延迟 ...

  8. Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed character class near index 0 解决方法: 要对切割字符进行转义\\

    使用str.split("[",15)时,出现Exception in thread "main" java.util.regex.PatternSyntaxE ...

  9. PySpark笔记

    spark源码位置:https://github.com/apache/spark Spark Core核心RDD及编程 什么是RDD:1.是一个抽象类不能直接使用,在子类中实现抽象方法是一个抽象类不 ...

  10. Go语言学习之3 流程控制、函数

    主要内容: 1. strings和strconv使用2. Go中的时间和日期类型3. 指针类型4. 流程控制5. 函数详解 1. strings和strconv使用 //strings . strin ...