1. 作业需求
借助socket模块实现server端和client端的交互,拟实现网盘上传下载的功能:
上传: client端发送请求,把本地的文件上传给server端,server端负责接收,然后server端的一个文件中写入client端上传的文件内容;
下载: client端发送请求,想要下载server端某文件,server端接收请求后,给客户端发送该文件的内容(按字节读取文件内容,然后边读边发送给客户端)
之前自己试着写了一个:
1. 版本一(不完善--bymyself)
# server.py import socket import pickle sk=socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(("127.0.0.1",8080)) sk.listen() conn,addr=sk.accept() while True: head=conn.recv(1024) head=pickle.loads(head) file_name=head["filename"] file_size=head["filesize"] file_type=head["filetype"] file_path=head["filepath"] file_path=file_path+'\'+file_name+'.'+file_type with open(file_path, 'wb') as f: while file_size>0: content=conn.recv(1024) f.write(content) file_size-=1024 conn.close() sk.close()
# client.py import socket import pickle sk=socket.socket() sk.connect(("127.0.0.1",8080)) while True: head={"filename":"test","filesize":2048,"filetype":"txt",'filepath':"E:pyhtonworkspacepy3-praticePycharm_workspacepython_fullstackweek8day07"} file_size = head["filesize"] head=pickle.dumps(head) sk.send(head) with open("xixi","rb") as f: while file_size>0: content=f.read(1024) sk.send(content) file_size-=1024 sk.close()
也可以实现上传功能(其实就是client端发送一个请求,想把本地的某一个文件上传给server端),server端可以接收这个文件,然后写入,但是有一点点问题,到文件的最后 会多些一点乱七八糟的东西,这个问题没有解决
2. 版本二(Eva-J)
# server.py import socket import pickle import struct sk=socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 防止重启服务器发生错误 sk.bind(("127.0.0.1",8080)) # server端绑定IP地址和端口号 sk.listen() conn,addr=sk.accept() buffer=1024 # 读取文件的字节大小是1024 head_len_bytes=conn.recv(4) # 因为client端需要发送head报头的长度 int类型借助struct模板转为byets类型发送,占有固定的长度四个字节 head_len=struct.unpack("i",head_len_bytes)[0] # 把报头head的字节长度 借助struct模块转为的bytes 再使用struct的unpack转为int的整数,代表head报头的字节长度 head_bytes=conn.recv(head_len) # 字节类型,client端把报头head使用pickle的dumps成bytes类型,直接发送,server直接接收head的bytes类型 head=pickle.loads(head_bytes) # 把bytes类型的报头head使用pickle反序列化为原来的字典类型 filesize=head["filesize"] # 得到client端需要上传的文件的长度 filename=head["filename"]+"."+head["filetype"] # 得到client端需要上传的文件的文件名 with open(filename,"wb") as f: # 由于对于一些视频,音频等文件是无法按行读的,所以需要使用按照直接读,所以文件的打开方式都是rb 或者wb这种以二进制的方式进行的 while filesize>=0: # 当还有需要读取的字节数,就不断地按照特定长度的字节读取文件内容,然后写到server端的同名文件中 if filesize>=buffer: # 剩余的需要读取的文件字节数大于buffer时 每次就按照buffer字节来读 content=conn.recv(buffer) f.write(content) filesize-=buffer else: content=conn.recv(filesize) f.write(content) filesize-=buffer conn.close() sk.close()
# client.py import socket import pickle import struct import os sk=socket.socket() sk.connect(("127.0.0.1",8080)) buffer=1024 # 设置文件读取的字节数 head={"filename":"2018ASID格式要求","filetype":"docx","filepath":r"F:厦大课程-研二研二上学期2018ASID格式要求","filesize":None} # 定制报头信息 filepath=os.path.join(head["filepath"],(head["filename"]+"."+head["filetype"])) # 拼接路径,其实就是所要上传的文件路径 filesize=os.path.getsize(filepath) # 得到所要上传的文件的字节数大小 head["filesize"]=filesize head_bytes=pickle.dumps(head) # head想要从client端传到server端,网络传输必须序列化(pickle的结果时bytes,也可以使用json 序列化的结果是str) head_bytes_len=len(head_bytes) # head报头字节数的大小,因为网络传输都是字节数,必须要告诉对方需要接收多少字节才能准确接收到该报头信息 head_bytes_len_bytes=struct.pack("i",head_bytes_len) # 把bytes类型的head 对应的字节长度这个整数int使用struct的pack模块转化为字节,都是对应四个字节 # server端只需要接收四个字节,就可以拿到head字节数的长度,然后再接收这个 字节数的长度 就可以完全拿到head信息 sk.send(head_bytes_len_bytes) # 发送head报头字节数长度对应的字节(4个 里面其实代表的是表头的长度信息) sk.send(head_bytes) # 接下来发送head报头信息(字节类型的,server首先接收4个字节拿到head字节长度,接着接收这个字节长度 拿到head信息,bytes类型的,然后使用pickle反序列化成字典类型的head) with open(filepath,'rb') as f: # 打开filepath对应的文件---本地文件,,server端把需要上传的本地文件 写在py文件下的同名文件中 # 由于视频,音频等文件需要按照字节来读取文件,所以文件打开的方式是rb while filesize>=0: if filesize>=buffer: content=f.read(buffer) # 读的内容也是二进制,bytes类型 sk.send(content) filesize-=buffer else: content=f.read(filesize) sk.send(content) filesize-=buffer sk.close()
运行结果:
其实我只实现了上传功能,下载的原理完全一样的,直接把server 端和client端所做的工作互换就可以啦~
而且还可以加上比较人性化的交互功能,交给用户选择上传还是下载,选择路径,文件名,这样就显得高大上啦~