1. 作业需求

借助socket模块实现server端和client端的交互,拟实现网盘上传下载的功能:

上传: client端发送请求,把本地的文件上传给server端,server端负责接收,然后server端的一个文件中写入client端上传的文件内容;

下载: client端发送请求,想要下载server端某文件,server端接收请求后,给客户端发送该文件的内容(按字节读取文件内容,然后边读边发送给客户端)

之前自己试着写了一个:

1. 版本一(不完善--bymyself)



#server.py
importsocketimportpickle
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()whileTrue:
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-=1024conn.close()
sk.close()
View Code


#client.py
importsocketimportpickle
sk=socket.socket()
sk.connect(("127.0.0.1",8080))whileTrue:
head={"filename":"test","filesize":2048,"filetype":"txt",'filepath':"E:\pyhtonworkspace\py3-pratice\Pycharm_workspace\python_fullstack\week8\day07"}
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-=1024sk.close()
View Code

也可以实现上传功能(其实就是client端发送一个请求,想把本地的某一个文件上传给server端),server端可以接收这个文件,然后写入,但是有一点点问题,到文件的最后 会多些一点乱七八糟的东西,这个问题没有解决

2. 版本二(Eva-J)

#server.py
importsocketimportpickleimportstruct
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-=bufferelse:
content=conn.recv(filesize)
f.write(content)
filesize-=buffer
conn.close()
sk.close()
#client.py
importsocketimportpickleimportstructimportos
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-=bufferelse:
content=f.read(filesize)
sk.send(content)
filesize-=buffer
sk.close()

运行结果:


其实我只实现了上传功能,下载的原理完全一样的,直接把server 端和client端所做的工作互换就可以啦~

而且还可以加上比较人性化的交互功能,交给用户选择上传还是下载,选择路径,文件名,这样就显得高大上啦~