读文件通过udp分片发送的目的端:(包含错误的分片包)
#!/usr/bin/python
# -*- coding: utf-8 -*-
#python send_100frag_file.py -p 55432 -f snatdownload
import argparse
import logging
from scapy.all import *
# Define the maximum size of a packet
MAX_SIZE = 65500
# Define the IP addresses and ports
src_ip = '11.0.0.1'
dst_ip = '11.0.0.2'
src_port = 12345
dst_port = 54321
# Define the payload data
payload_data = 'A' * (MAX_SIZE - 20 - 8)
payload_data = 'A' * MAX_SIZE
# Create the IP header
# 创建参数解析器对象
parser = argparse.ArgumentParser(description="UDP数据接收脚本,可指定端口号和发送数据的文件名: python send_100frag_file.py -p 55432 -f snatdownload")
# 添加端口号参数,指定参数名为port,类型为整数,帮助信息为指定接收UDP数据的端口号,默认值设为9999
parser.add_argument('-p', '--port', type=int, help="指定接收UDP数据的端口号", default=9999)
# 添加文件名参数,指定参数名为filename,类型为字符串,帮助信息为指定保存接收到数据的文件名,默认值设为'received_data.data'
parser.add_argument('-f', '--filename', type=str, help="指定发送数据的文件名", default='input_data.data')
parser.add_argument('-i', '--ipid', type=int, help="指定ip id", default=11111)
# 解析命令行参数
args = parser.parse_args()
# 获取解析后的端口号和文件名
port = args.port
filename = args.filename
ipid = args.ipid
ip_header = IP(src=src_ip, dst=dst_ip, id = ipid)
# Create the UDP header
#udp_header = UDP(sport=src_port, dport=dst_port+int(sys.argv[1]))
udp_header = UDP(sport=src_port, dport=port)
# Combine the headers and payload
# 获取文件大小,用于后续判断是否发送完成
file_size = os.path.getsize(filename)
sent_size = 0
# 以二进制模式打开文件,方便处理各种类型的文件
with open(filename, 'rb') as file:
while True:
packet = None
# 每次读取1024字节的数据块,可根据实际网络情况和文件大小调整这个值
payload_data = file.read(MAX_SIZE)
if not payload_data:
break
# 发送消息,需将消息编码为字节类型,因为网络传输的数据是字节流形式
ip_header = IP(src=src_ip, dst=dst_ip, id = ipid)
packet = ip_header / udp_header / payload_data
fragments = fragment(packet, fragsize=656)
for i, frag in enumerate(fragments):
print('Fragment ' + str(i+1) + ': ' + str(len(frag)) + ' bytes')
#print(fragment.show())
if i < len(fragments) - 0:
send(frag, verbose=False)
if i >= 50:
frag[IP].flags=0
send(frag, verbose=False)
ipid += 1
sent_size += len(payload_data)
print("发送 {} 字节, 已发送 {} 字节,共 {} 字节".format(len(payload_data), sent_size, file_size))
end_marker = "END".encode('utf-8')
packet = ip_header / udp_header / end_marker
send(packet, verbose=False)
print("发送结束标志,发送结束")
接收指定端口收到的udp报文保存到本地文件,通过比较发送文件和接收文件的内容测试中间转发设备是否正确完整的处理了分片和重组:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#python recv_dgram.py -p 55432 -f my_data.txt
import socket
import argparse
import logging
# 创建参数解析器对象
parser = argparse.ArgumentParser(description="UDP数据接收脚本,可指定端口号和保存数据的文件名: python recv_dgram.py -p 55432 -f my_data.txt")
# 添加端口号参数,指定参数名为port,类型为整数,帮助信息为指定接收UDP数据的端口号,默认值设为9999
parser.add_argument('-p', '--port', type=int, help="指定接收UDP数据的端口号", default=9999)
# 添加文件名参数,指定参数名为filename,类型为字符串,帮助信息为指定保存接收到数据的文件名,默认值设为'received_data.data'
parser.add_argument('-f', '--filename', type=str, help="指定保存接收到数据的文件名", default='received_data.data')
# 解析命令行参数
args = parser.parse_args()
# 获取解析后的端口号和文件名
port = args.port
file_name = args.filename
# 创建UDP套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定本地地址和端口,这里假设绑定到本地所有可用地址(空字符串)的9999端口,你可以按需修改
local_address = ('', port)
udp_socket.bind(local_address)
udp_socket.settimeout(5) # 设置超时时间为5秒
# 以二进制写入模式打开文件,方便处理各种类型的数据(文本、二进制等)
tot_len = 0
with open(file_name, 'wb') as file:
while True:
try:
# 接收数据报,设置缓冲区大小为1024字节,可根据实际情况调整大小
udp_socket.settimeout(5) # 设置超时时间为5秒
data, sender_address = udp_socket.recvfrom(65530)
if not data:
break
if len(data) == 3 and data.decode('utf-8') == "END":
logging.info("完成接收")
print("完成接收")
break
tot_len += len(data)
# 将接收到的数据写入文件
file.write(data)
print("从 {}接收到{}字节, total length: {}".format(sender_address, len(data), tot_len))
except socket.timeout:
logging.info("接收超时,可能暂时没有消息到达")
continue
except socket.error as e:
print("接收UDP数据报时出错: {}".format(e))
break
# 关闭套接字
udp_socket.close()
print("已成功将接收到的UDP数据报保存到 {} 文件中。".format(file_name))