一、wireshark抓包工具
1、安装wireshark
2、使用wireshark
- 找到电脑IP地址对应的选项
- 开始捕获
- 获取捕获列表
- 过滤条件 udp.port== 8000或者ip.addr == 172.16.1.1发送数据
- 点击数据行
- 看数据包详细信息
- Ethernet II链路层数据包头,16进制显示
- 开始/停止捕获按钮
二、TFTP协议(简单文件传输协议)
1、特点
- 简单
- 资源少
- 适合小文件传输
- 适合在局域网内使用
- 端口号69
- UDP实现
2、下载过程
服务器默认监听端口号:69
客户端向服务器69端口发送请求----》服务器同意----》开启一个新端口进行数据传输
(1)序号
服务器找到文件会把文件数据通过TFTP协议发给客户端
文件过大会分批传输(512字节传输)
发送次数可能会比较多。需要排序,会多发2byte存放序号放在512byte前从1开始
(2)操作码
如果文件不存在,服务器发送错误信息回来
为了区分,使用2byte来表示数据包功能(操作码)放在序号前
(3)应答
服务器收到数据后会发给客户端一个数据包用作应答已经收到
(4)结束
当客户端收到数据小于516字节就结束
三、代码
#coding=utf-8#采用utf-8编码
from socket import *
import struct
import sys
if len(sys.argv) != 2: #包含命令行参数列表
print('*'*30) #分割线
print("tips:")
print("python xxxx.py 192.168.1.1")
print('*'*30) #分割线
exit()#终止进程
else:
ip = sys.argv[1]
# 创建udp套接字
udpSocket = socket(AF_INET, SOCK_DGRAM)
#构造下载请求数据
cmd_buf = struct.pack("!H8sb5sb",1,"test.jpg",0,"octet",0)#struck包里面的函数!H16进制8s8个字符b二进制5个字符二进制
#发送下载⽂件请求数据到指定服务器
sendAddr = (ip, 69)
udpSocket.sendto(cmd_buf, sendAddr)
p_num = 0
recvFile = ''
while True:
recvData,recvAddr = udpSocket.recvfrom(1024)
recvDataLen = len(recvData)
cmdTuple = struct.unpack("!HH", recvData[:4]) #取四个字节
cmd = cmdTuple[0] #操作码
currentPackNum = cmdTuple[1] #序号
if cmd == 3: #是否为数据包
# 如果是第⼀次接收到数据,那么就创建⽂件
if currentPackNum == 1:
recvFile = open("test.jpg", "a")
# 包编号是否和上次相等
if p_num+1 == currentPackNum:
recvFile.write(recvData[4:])
p_num +=1
print('(%d)次接收到的数据'%(p_num))
ackBuf = struct.pack("!HH",4,p_num))#ack应答包
udpSocket.sendto(ackBuf, recvAddr)
# 如果收到的数据⼩于516则认为出错
if recvDataLen<516:
recvFile.close()
print ('已下载')
break
elif cmd == 5:
#是否为错误应答
print ("error num:%d"(%currentPackNum))
break
udpSocket.close()#关闭套接字
# -*- coding:utf-8 -*-
from socket import *
import os
import struct
import time
from pip._vendor.distlib.compat import raw_input
#1、获取下载文件名字
downloadFileName = raw_input("请输入要下载的文件名:")
#2、创建套接字
udpSocket = socket(AF_INET, SOCK_DGRAM)
#3、发送下载请求
requestFileData = struct.pack("!H%dsb5sb" % len(downloadFileName), 1, downloadFileName, 0, "octet", 0)#构造下载请求数据
udpSocket.sendto(requestFileData, ("192.168.119.215", 69))
flag = True # 表示能够下载数据,是false那么就删除
num = 0
f = open(downloadFileName, "w")
while True:
# 3.1接收服务发送回来的应答数据
responseData = udpSocket.recvfrom(1024)
recvData, serverInfo = responseData
#取出操作码和序号
opNum = struct.unpack("!H", recvData[:2])
packetNum = struct.unpack("!H", recvData[2:4])
if opNum[0] == 3: # 3为数据包
num = num + 1 # 文件的序号值
# 如果一个下载的文件特别大,即接收到的数据包编号超过了2个字节的大小
# 那么会从0继续开始,所以这里需要判断,如果超过了65535 那么就改为0
if num == 65536:
num = 0
# 判断这次接收到的数据的包编号是否是 上一次的包编号的下一个
# 如果是才会写入到文件中,否则不能写入(因为会重复)
if num == packetNum[0]:
# 把收到的数据写入到文件中
f.write(recvData[4:])
num = packetNum[0]
# 整理ACK的数据包
ackData = struct.pack("!HH", 4, packetNum[0])
udpSocket.sendto(ackData, serverInfo)
#操作码为5时错误
elif opNum[0] == 5:
print("没有这个文件")
flag = False
#数据小于516时结束
if len(recvData) < 516:
break
if flag == True:
f.close()
else:
os.unlink(downloadFileName) # 如果没有要下载的文件,那么就需要把刚刚创建的文件进行删除
可能出现的问题:
1、struct.error: argument for ‘s’ must be a bytes object----》需要更改将文件名使用utf-8编码
requestFileData = struct.pack("!H%dsb5sb" % len(downloadFileName), 1, downloadFileName.encode('utf-8'), 0, "octet".encode('utf-8'), 0)
2、改完出现write() argument must be str, not bytes—》将文件打开方式修改为wb