基于序列化技术(Protobuf)的socket文件传输
好像好久都没更博文了,没办法,最近各种倒霉事情,搞到最近真的没什么心情,希望之后能够转运吧。
言归正传,这次我要做的是基于序列化技术的socket文件传输来无聊练一下手。
一.socket文件传输
之前我所做的服务器和客户端的tcp/udp通信都是以字符串流来进行单工的,双工的传输,其实关于文件传输的原理也差不多,我的主要方法是通过文件迭代器遍历文件流,并将其读取转化为字符串流,然后将字符串流从服务器端发送,然后客户端在缓冲区中接收数据流,然后并把它写入文件当中,从而实现文件的传输,在下面程序当中,我仅仅是实现由服务器分发,客户端除了接收什么都不做的简单程序。
服务器端:
#!/usr/bin/env python #-*- coding:utf-8 -*- from socket import * from time import ctime import os HOST = '' PORT = 21567 BUFSIZ = 4096 ADDR = (HOST,PORT) tcpSerSock = socket(AF_INET,SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) filename = raw_input('Please input the file name:') while True: print 'waiting for connection...' tcpCliSock,addr = tcpSerSock.accept() print '...connected from:',addr print '向客户端发送文件...' f = open(filename,'r') for eachLine in f: tcpCliSock.send('%s' % eachLine) f.close() tcpCliSock.close() print 'Done!' tcpSerSock.close()
客户端:
#!/usr/bin/env python #-*- coding:utf-8 -*- from socket import * HOST = 'localhost' PORT = 21567 BUFSIZ = 4096 ADDR = (HOST,PORT) tcpCliSock = socket(AF_INET,SOCK_STREAM) tcpCliSock.connect(ADDR) filename = raw_input('Please input the filename you will write to:') f = open(filename,'a') #以追加模式打开文件 print '正在写文件....' while True: data = tcpCliSock.recv(BUFSIZ) if not data: break f.write(data) f.close() print 'Done!' tcpCliSock.close()
还有一种比较方便的方法是用框架SocketServer来实现文件传输的功能,相当于重新实现linux的scp命令。
服务器端:
#!/usr/bin/env python #-*-coding:utf-8-*- import SocketServer addr = ('',21568) class DocHandler(SocketServer.StreamRequestHandler): def handle(self): doc_len = ord(self.rfile.read(1)) #文件的第一个字符代表文件名的长度 name = self.rfile.read(doc_len) print "接收文件:%s" % name f = open('../'+name,'w') cont = self.rfile.read(4096) while cont: f.write(cont) cont = self.rfile.read(4096) f.close() print "Done :%s" % name server = SocketServer.TCPServer(addr,DocHandler) #利用tcp传输 server.serve_forever()
客户端:
#!/usr/bin/env python #-*- coding:utf-8 -*- from socket import * import os.path addr = ('',21568) def get_header(name): n_len = len(name) assert n_len < 250 #assert语句,代表一个肯定的判定语句,如果为false,会抛出AssertError的异常 return chr(n_len) + name def send_file(name): basename = os.path.basename(name) header = get_header(basename) cont = open(name).read() s = socket(AF_INET,SOCK_STREAM) s.connect(addr) s.sendall(header) s.sendall(cont) s.close() if __name__ == '__main__': filename = raw_input("请输入你要传输的文件名:") send_file(filename)
二.序列化技术
所谓序列化技术,自己可以google一下。
这次我用的序列化技术的框架是google 的protocol buffer(简称protobuf),关于它的详细介绍,可以看看它的介绍,文档和API,它是一种和平台无关,语言无关的技术。
这次的程序我本来想用C++写的,但无奈环境搭建失败,用不了,只好再用python的。
首先为了避免重复劳动,我使用了它原来example的.proto文件,做一个关于名片传输的程序。
addressbook.proto
// See README.txt for information and build instructions. package tutorial; option java_package = "com.example.tutorial"; option java_outer_classname = "AddressBookProtos"; message Person { required string name = 1; required int32 id = 2; // Unique ID number for this person. optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } // Our address book file is just one of these. message AddressBook { repeated Person person = 1; }
输入指令来生成addressbook_pb2.py:(注:如果是C++,则python换为cpp;如果是Java,则python换为java)
protoc -I=./ --python_out=./ addressbook.proto
然后先尝试弄个单机版出来吧。 根据官方的demo修改整合出来的。
add_person.py
#! /usr/bin/python # See README.txt for information and build instructions. import addressbook_pb2 import sys # This function fills in a Person message based on user input. def PromptForAddress(person): person.id = int(raw_input("Enter person ID number: ")) person.name = raw_input("Enter name: ") email = raw_input("Enter email address (blank for none): ") if email != "": person.email = email while True: number = raw_input("Enter a phone number (or leave blank to finish): ") if number == "": break phone_number = person.phone.add() phone_number.number = number type = raw_input("Is this a mobile, home, or work phone? ") if type == "mobile": phone_number.type = addressbook_pb2.Person.MOBILE elif type == "home": phone_number.type = addressbook_pb2.Person.HOME elif type == "work": phone_number.type = addressbook_pb2.Person.WORK else: print "Unknown phone type; leaving as default value." # Main procedure: Reads the entire address book from a file, # adds one person based on user input, then writes it back out to the same # file. filename = raw_input('Please input the file name:') address_book = addressbook_pb2.AddressBook() # Read the existing address book. try: f = open(filename, "rb") address_book.ParseFromString(f.read()) f.close() except IOError: print sys.argv[1] + ": File not found. Creating a new file." # Add an address. PromptForAddress(address_book.person.add()) # Write the new address book back to disk. f = open(filename, "wb") f.write(address_book.SerializeToString()) f.close()
list_person.py
#! /usr/bin/python # See README.txt for information and build instructions. import addressbook_pb2 import sys # Iterates though all people in the AddressBook and prints info about them. def ListPeople(address_book): for person in address_book.person: print "Person ID:", person.id print " Name:", person.name if person.HasField('email'): print " E-mail address:", person.email for phone_number in person.phone: if phone_number.type == addressbook_pb2.Person.MOBILE: print " Mobile phone #:", elif phone_number.type == addressbook_pb2.Person.HOME: print " Home phone #:", elif phone_number.type == addressbook_pb2.Person.WORK: print " Work phone #:", print phone_number.number # Main procedure: Reads the entire address book from a file and prints all # the information inside. filename = raw_input("Please input the filename:") address_book = addressbook_pb2.AddressBook() # Read the existing address book. f = open(filename, "rb") address_book.ParseFromString(f.read()) f.close() ListPeople(address_book)
运行过没有任何问题。
三.整合
好了,到了最后一步了, 实现的功能时,服务器端先要求你设定你自己要添加自己的名片信息,序列化,然后分发给客户端,客户端再把它反序列化,输出个人信息。
服务器端:
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 4 from socket import * 5 from time import ctime 6 import os 7 import addressbook_pb2 8 9 HOST = '' 10 PORT = 21567 11 BUFSIZ = 4096 12 ADDR = (HOST,PORT) 13 14 tcpSerSock = socket(AF_INET,SOCK_STREAM) 15 tcpSerSock.bind(ADDR) 16 tcpSerSock.listen(5) 17 18 #添加联系人函数 19 def PromptForAddress(person): 20 person.id = int(raw_input("Enter person ID number: ")) 21 person.name = raw_input("Enter name: ") 22 23 email = raw_input("Enter email address (blank for none): ") 24 if email != "": 25 person.email = email 26 27 while True: 28 number = raw_input("Enter a phone number (or leave blank to finish): ") 29 if number == "": 30 break 31 32 phone_number = person.phone.add() 33 phone_number.number = number 34 35 type = raw_input("Is this a mobile, home, or work phone? ") 36 if type == "mobile": 37 phone_number.type = addressbook_pb2.Person.MOBILE 38 elif type == "home": 39 phone_number.type = addressbook_pb2.Person.HOME 40 elif type == "work": 41 phone_number.type = addressbook_pb2.Person.WORK 42 else: 43 print "Unknown phone type; leaving as default value." 44 45 filename = raw_input('Please input the file name:') 46 47 address_book = addressbook_pb2.AddressBook() 48 49 try: 50 f = open(filename,"rb") 51 address_book.ParseFromString(f.read()) 52 f.close() 53 except IOError: 54 print filename + ": File not found. Creating a new file." 55 56 #添加联系人信息 57 PromptForAddress(address_book.person.add()) 58 59 #写进去 60 f = open(filename,"wb") 61 f.write(address_book.SerializeToString()) 62 f.close() 63 64 while True: 65 print 'waiting for connection...' 66 tcpCliSock,addr = tcpSerSock.accept() 67 print '...connected from:',addr 68 69 print '向客户端发送文件...' 70 f = open(filename,'rb') 71 72 for eachLine in f: 73 tcpCliSock.send('%s' % eachLine) 74 f.close() 75 tcpCliSock.close() 76 print 'Done!' 77 78 tcpSerSock.close()
客户端:
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 from socket import * 4 import addressbook_pb2 5 6 HOST = 'localhost' 7 PORT = 21567 8 BUFSIZ = 4096 9 ADDR = (HOST,PORT) 10 11 tcpCliSock = socket(AF_INET,SOCK_STREAM) 12 tcpCliSock.connect(ADDR) 13 14 #输出信息函数 15 def ListPeople(address_book): 16 for person in address_book.person: 17 print "Person ID:", person.id 18 print " Name:", person.name 19 if person.HasField('email'): 20 print " E-mail address:", person.email 21 22 for phone_number in person.phone: 23 if phone_number.type == addressbook_pb2.Person.MOBILE: 24 print " Mobile phone #:", 25 elif phone_number.type == addressbook_pb2.Person.HOME: 26 print " Home phone #:", 27 elif phone_number.type == addressbook_pb2.Person.WORK: 28 print " Work phone #:", 29 print phone_number.number 30 31 32 filename = raw_input('Please input the filename you will write to:') 33 address_book = addressbook_pb2.AddressBook() 34 35 f = open(filename,'ab') #以追加模式打开文件 36 print '正在写文件....' 37 while True: 38 data = tcpCliSock.recv(BUFSIZ) 39 if not data: 40 break 41 f.write(data) 42 43 f.close() 44 print 'Done!' 45 46 f = open(filename,"rb") 47 address_book.ParseFromString(f.read()) 48 f.close() 49 50 ListPeople(address_book) 51 52 tcpCliSock.close()
搞定!请多多指教!
转载请注明出处:http://www.cnblogs.com/sysu-blackbear/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步