组播发送脚本
python send_mcast.py -g 232.1.2.3 -p 12345
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import struct
import os
import argparse
import logging
# 组播组地址,范围是224.0.0.0 - 239.255.255.255,这里选用224.0.0.1作为示例
multicast_group = '232.1.2.3'
# 组播端口号,可自行按需选择合适的值,这里用12345
multicast_port = 12345
# 要发送的消息内容
message = "Hello, this is a multicast message example"
filename = "test.txt"
filename = "send_mcast.py"
# 创建命令行参数解析器
parser = argparse.ArgumentParser(description='组播消息接收程序')
# 组播组地址参数,默认值设为示例中的232.1.2.3,可通过命令行修改
parser.add_argument('-g', '--group', type=str, default='232.1.2.3', help='组播组地址')
# 组播端口号参数,默认值设为示例中的12345,可通过命令行修改
parser.add_argument('-p', '--port', type=int, default=12345, help='组播端口号')
#localaddress
#input file
parser.add_argument('-f', '--infile', type=str, default="", help='发送文件')
args = parser.parse_args()
multicast_group = args.group
multicast_port = int(args.port)
filename=args.infile if args.infile else filename
# TTL值,可根据实际需求调整,范围通常是1 - 255,这里设置为10
ttl_value = 10
# 创建UDP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 设置套接字选项,允许重用地址
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 设置IP数据报的TTL值,IPPROTO_IP表示IP协议层,IP_MULTICAST_TTL是设置TTL的选项
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_value)
#don't need to join group on sender
# 将套接字绑定到本地所有可用地址(用空字符串表示)和指定的组播端口
#sock.bind(('', multicast_port))
# 加入组播组
local_addr = socket.inet_aton('192.169.1.2')
group = socket.inet_aton(multicast_group)
mreq = struct.pack('4sL', group, socket.INADDR_ANY)
#mreq = struct.pack('4s4s', group, local_addr)
#sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# 发送组播消息,需将消息编码为字节类型,因为网络传输的数据是字节流形式
#sock.sendto(message.encode('utf-8'), (multicast_group, multicast_port))
# 获取文件大小,用于后续判断是否发送完成
file_size = os.path.getsize(filename)
sent_size = 0
# 以二进制模式打开文件,方便处理各种类型的文件
with open(filename, 'rb') as file:
while True:
# 每次读取1024字节的数据块,可根据实际网络情况和文件大小调整这个值
data = file.read(1472)
if not data:
break
# 发送组播消息,需将消息编码为字节类型,因为网络传输的数据是字节流形式
sock.sendto(data, (multicast_group, multicast_port))
sent_size += len(data)
print("发送 {} 字节, 已发送 {} 字节,共 {} 字节".format(len(data), sent_size, file_size))
end_marker = "END".encode('utf-8')
sock.sendto(end_marker, (multicast_group, multicast_port))
print("发送结束标志,发送结束")
# 关闭套接字
sock.close()
组播接收脚本
#python mcast_receiver.py -g 232.1.2.3 -p 12345 -i 20.0.0.2 -o /tmp/rec.log
#!/usr/bin/python
# -*- coding: utf-8 -*-
import argparse
import socket
import struct
import logging
# 配置日志记录基本设置,设置日志级别为INFO,日志格式以及输出到文件等
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='multicast_receiver.log',
filemode='w')
# 创建命令行参数解析器
parser = argparse.ArgumentParser(description='组播消息接收程序')
# 组播组地址参数,默认值设为示例中的232.1.2.3,可通过命令行修改
parser.add_argument('-g', '--group', type=str, default='232.1.2.3', help='组播组地址')
# 组播端口号参数,默认值设为示例中的12345,可通过命令行修改
parser.add_argument('-p', '--port', type=int, default=12345, help='组播端口号')
#localaddress
parser.add_argument('-i', '--local', type=str, default="20.0.0.2", help='本地地址')
#output file
parser.add_argument('-o', '--output', type=str, default="output.file", help='输出文件')
args = parser.parse_args()
# 获取命令行传入的组播组地址和组播端口号
multicast_group = args.group
multicast_port = int(args.port)
local_address = args.local
filename = args.output
# 创建UDP套接字
sock = None
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
logging.info("成功创建UDP套接字")
if sock is None:
raise Exception("创建套接字失败")
# 设置套接字选项,允许重用地址
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
logging.info("成功设置SO_REUSEADDR选项")
except socket.error as e:
logging.error("设置SO_REUSEADDR选项出错: "+e)
raise
# 将套接字绑定到本地所有可用地址(用空字符串表示)和指定的组播端口
try:
sock.bind(('', multicast_port))
logging.info("成功将套接字绑定到端口: {}".format(multicast_port))
except socket.error as e:
logging.error("套接字绑定出错: {}".format(e))
raise
# 加入组播组
try:
group = socket.inet_aton(multicast_group)
#mreq = struct.pack('4sL', group, socket.INADDR_ANY)
local_addr = socket.inet_aton('20.0.0.2')
local_addr = socket.inet_aton(local_address)
mreq = struct.pack('4s4s', group, local_addr)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
logging.info("成功加入组播组 {}".format(multicast_group))
except socket.error as e:
logging.error("加入组播组出错: {}".format(e))
raise
# 循环接收组播消息
tot_len = 0
with open(filename, 'wb') as file:
while True:
try:
sock.settimeout(5) # 设置超时时间为5秒
data, address = sock.recvfrom(1500)
#logging.info("从 {} 接收到消息".format(address))
if data.decode('utf-8') == "END":
logging.info("完成接收")
print("完成接收")
break
tot_len += len(data)
file.write(data)
print("从 {}接收到{}字节, total length: {}".format(address, len(data), tot_len))
except socket.timeout:
logging.info("接收超时,可能暂时没有组播消息到达")
continue
except socket.error as e:
logging.error("接收组播消息出错: {e}".format(e))
break
finally:
# 关闭套接字释放资源
if sock is not None:
try:
sock.close()
logging.info("套接字已关闭")
except socket.error as e:
logging.error("关闭套接字时出错: {}".format(e))