一个简单的UDP服务端与客户端
服务端:
1 from socket import * 2 #创建套接字 3 udp_server = socket(AF_INET,SOCK_DGRAM) 4 5 msg_server = ("",8877) 6 #绑定ip地址和端口 7 udp_server.bind(msg_server) 8 9 while True: 10 #接受消息,注意此处用的是 recvfrom() 11 msg_client = udp_server.recvfrom(1024) 12 13 print("新客户端已连接--->>>") 14 15 if len(msg_client) != 0: 16 #msg_client中有两个值,第一个表示收到的消息的内容,第二个表示客户端的IP地址和端口的信息 17 print("%s:%s" % (msg_client[1], msg_client[0].decode("utf-8"))) 18 else: 19 break 20 21 udp_server.close()
客户端:
from socket import * sock = socket(AF_INET,SOCK_DGRAM) addr_msg = ("192.168.1.104",8877) #连接服务端 sock.connect(addr_msg) while True: msg_send = input("请输入要发送的信息:") if msg_send =='q': break #注意此处用的是sendto() 其中第一个参数表示要发送的消息的内容,第二个参数表示服务端的IP地址和端口的元组 sock.sendto(msg_send.encode("utf-8"),addr_msg) sock.close()
基于UDP的tftp文件传输
客户端:
1 from socket import * 2 import struct 3 import os 4 5 def main(): 6 file_name = input("请输入要下载的文件名:") 7 #建立一个UDP的套接字 8 udpsocket = socket(AF_INET,SOCK_DGRAM) 9 #创建一个tftp的下载请求 H表示第一个参数占用两个字节,d表示对应的参数为bite类型 10 # %ds 表示文件名占用的字符个数,如后面的octet占用了5个字符,所以写成5s 11 # octet是tftp传输的一种模式,它决定了传输数据的格式,还有其他的几种tftp传输的模式 12 #构造包使用的是struck中的pack ,其中的 H 表示使参数占用两个字节 13 request_header = struct.pack("!H%dsb5sb"%len(file_name),1,file_name.encode('utf-8'),0,b"octet",0) 14 server_msg = ("192.168.0.100",69) 15 #发送一个下载文件的请求 16 udpsocket.sendto(request_header,server_msg) 17 18 # 这里必须以wb的格式打开文件,否则文件不能正常显示 19 f = open(file_name,"wb") 20 21 # num表示的是数据块的编号,这里使得其初始值为0 22 num = 0 23 #此处建立一个标记,以确认传输过程中没发生错误 24 mask = True 25 26 while True: 27 # 接收服务端发来的数据包 28 response_data = udpsocket.recvfrom(1024) 29 recv_data, server_info = response_data 30 31 # 此处用struct中的unpack来解包 H 表示数据占用两个字节长度 32 # 这里的前两个字节中存储的是操作码 33 operation_num = struct.unpack("!H",recv_data[:2]) 34 # 这里的第三个和第四个字节中存储的是数据块的编号 35 package_num = struct.unpack("!H",recv_data[2:4]) 36 print(package_num[0]) 37 38 # 操作码为3时表示接收到的信息是服务端收到响应而发给客户端的数据包 39 if operation_num[0] == 3: 40 #这一次收到的值应该是在上一次收到的值的基础上加上一后的结果 41 num =num + 1 42 #此处表示一旦num的值超过了它2个字节所表示的值的范围,则让它从0开始再来重复一遍(主要是因为操作码只能占2个字节) 43 if num == 65535: 44 num = 0 45 # 进行块编号的确认 46 if num == package_num[0]: #此处取下标是因为此时的package_num表示的是一个元组,而块编号是元组中的第一个数据 47 # 将收到的具体数据写入到文件中 48 f.write(recv_data[4:]) 49 #块编号的确认每次都要在之前的值上加1 50 num = package_num[0] 51 #构造一个响应包 操作码为4 发送的数据就是加上1后的块编号 52 ack_data = struct.pack("!HH",4,package_num[0]) 53 #发送响应包 54 udpsocket.sendto(ack_data, server_info) 55 56 elif operation_num[0] == 5: 57 print("Error!") 58 #若发生错误信息,则将标记的值改成False , 以便后续操作删除这个文件 59 mask = False 60 f.close() 61 62 # 一个数据包的最大长度为 4+512 = 516 位,如果数据包小于这个数值,则表明这已经是最后一次的数据传输了 63 if len(recv_data) < 516: 64 print("Finish!") 65 break 66 # mask标记的值为True则表示文件传输的过程中没有发生错误,就将本地接收到的文件关闭保存 67 if mask == True: 68 f.close() 69 70 else: 71 # mask标记的值为False则表示文件传输的过程中出现了错误信息,则将已经接收到的文件删除 72 os.remove(file_name) 73 74 if __name__ == "__main__": 75 main()
至于服务端可以用 tftpd32 这个软件来模拟实现
关于TFTP协议
TFTP协议是一种基于UDP的小型文件传输协议,它不具备FTP的许多功能
TFTP的端口号为69
一个数据包在接受的过程中最大长度为512+2+2=516字节,所以当它的长度小于516字节时便可以判断出这是最后一次的数据传输
上面构造数据包时使用的H的意思是,让它所代表的参数占用两个字节的长度,从而使数据包符合TFTP协议的要求