python核心编程:udp实现TFTP上传下载

# -*- coding:utf-8-*-
"""
udp实现tftp上传下载python2.7
1.首先要获得下载的文件名,使用input让用户输入
2.创建套接字
3.打包pack,用于将Python的值根据格式符,转换为字符串
4.解包,用于将字节流转换成python数据类型
5.判断ack的值
6.判断操作码(1.下载 2.上传 3.数据包data 4.确认码ack 5.错误)
"""

from socket import *
from struct import *
import os

def download():
    #获取文件名
    fileName = raw_input("请输入需要下载的文件名:")
    #查看当前目录下所有文件
    allfilename = os.listdir('.')
    #print allfilename

    def caozuo():
        # 创建套接字
        udpscoket = socket(AF_INET, SOCK_DGRAM)
        addr = ("192.168.123.1", 69)
        # 打包"!"表示大端,也就是高位放在低端,H占位(2个字节),%ds是文件名的字符数,b表示占一位字节
        requestFileData = pack("!H%dsb5sb" % len(fileName), 1, fileName, 0, 'octet', 0)
        # 发送下载请求
        udpscoket.sendto(requestFileData, addr)
        # 下载过程就是打开文件写入文件的过程,b表示二进制方式读写操作等
        f = open(fileName, "wb")
        flag = True
        num = 0
        while True:
            # 接收服务器应答的数据
            requestData = udpscoket.recvfrom(1024)
            # 服务器返回的数据
            severdata = requestData[0]
            # 服务器返回的IP地址和端口号
            severinfo = requestData[1]
            # print severdata,severinfo
            # 操作码
            cmd = unpack("!H", severdata[:2])
            # 块编号
            datanum = unpack("!H", severdata[2:4])
            print "块编号为:%d" % datanum[0]
            # 此时的cmd是一个元组,需要用坐标取值,如果返回的是文件内容
            if cmd[0] == 3:
                print "下载操作执行完毕!"
                # 计算本次接收的序号值,在上次基础上加一
                num += 1
                # 二进制数最大65535,因此超过此值需要重置
                if num == 65536:
                    num = 0
                elif num == datanum[0]:
                    # 数据前两个字节是操作码,操作码之后的两个字节是序号,不写入文件
                    f.write(severdata[4:])
                    num = datanum[0]
                # 将ack数据包发送给服务器
                ackdata = pack("!HH", 4, datanum[0])
                udpscoket.sendto(ackdata, severinfo)
            elif cmd[0] == 5:
                print "没有这个文件"
                flag = False

            if len(severdata) < 516:
                break
            # 判断是否存在该文件
            if flag == True:
                f.close()
            else:
                os.unlink(fileName)  # 不存在则删除创建的文件
    if fileName in allfilename:
        print "已有此文件,继续下载将覆盖原文件,是否继续? Y继续,按任意键退出程序"
        yn = raw_input("请输入:")
        yn.lower()
        if yn == "y" or yn == "Y":
            #print "cccc"
            caozuo()
            #print "aaaa"
        else:
            print "已退出"
            exit()
    else:
        caozuo()



def upload():
    #获取想要上传的文件名
    fileName = raw_input('请输入需要上传的文件名:')
    udpsocket = socket(AF_INET,SOCK_DGRAM)
    addr = ("192.168.123.1",69)
    senddata1 = pack("!H%dsb5sb"%len(fileName),2,fileName,0,'octet',0)
    udpsocket.sendto(senddata1,addr)#发送上传请求
    f = open(fileName,'rb')
    recvfile = udpsocket.recvfrom(1024)
    recvdata,recvinfo = recvfile
    cmd = unpack("!H",recvdata[:2])
    datanum = unpack("!H",recvdata[2:4])
    datanum = 0#表示接收的序列号
    while True:
        if cmd[0] == 5:
            print "上传失败!"
            break
        else:
            fr = f.readline(512)#传输数据是一次传输512个字节的
            #打包,把数据传送到随机端口
            senddata2 = pack("!HH",3,datanum) + fr
            udpsocket.sendto(senddata2,recvinfo)
            #接收服务器返回的数据
            recvdata,recvinfo = udpsocket.recvfrom(1024)
            #解包
            cmd = unpack("!H", recvdata[:2])
            datanum = unpack("!H", recvdata[2:4])
            if cmd[0] == 5:
                print "上传失败!"
                break
            #判断是否传送完成,2个字节的操作码,2个字节的序列号,512个字节的数据长度共516,因此小于这个值则判断为完成
            if len(senddata2) < 516 or datanum[0] != fileName:
                print "上传成功!"
                break
            datanum += 1
    f.close()
    udpsocket.close()

def main():
    #try:
        caozuo = input("请输入想要进行的操作:1.下载 2.上传\n》")
        if caozuo == 1:
            download()
        elif caozuo == 2:
            upload()
        else:
            print "输入错误!"
    #except:
        #print "输入格式错误!"

if __name__ == '__main__':
    main()

你可能感兴趣的:(python核心编程)