py6.6
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#文件的上传和下载: #自定义一个字典,让用户选择功能(上传/下载)。将要上传的文件路径传入,找到文件名,打开文件,计算文件大小, #将功能、文件名、文件大小定义成一个字典,通过stuct模块和json模块固定字典的长度为4并序列化,以便防止发送 #时与后面要发送的文件内容出现黏包现象。通过循环将文件按照固定的大小(1024)分次发送到服务端。服务端也根据收到 #的文件大小来分次接收。 #struct模块 # import struct # # # r = struct.pack('i',10) #将10变成一个固定的长度4的字符串 # print(len(r)) # print(r) # print(struct.unpack('i',r))#解开,得到的是一个元祖形式,原数值在元祖的第0个元素。 # import json # dic = {'a': '1'} # r = json.dumps(dic) # print(json.loads(r)) server: import socket import struct import json sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen(5) conn , addr = sk.accept() r = conn.recv(4) #接收从客户端处已经被struct模块将原字典长度转换成固定长度的bytes(长度为4) len_dic = struct.unpack('i',r)[0]#将这个bytes解开,变成一个元祖,要的是第一个元素-->原字典的长度。 # print(len_dic) str_dic = conn.recv(len_dic).decode('utf-8')#根据原字典的长度来接收被序列化了字典 # print(str_dic) str_dic = json.loads(str_dic)#将字典反序列化 if str_dic['opt'] == 'upload': name = 'new_' + str_dic['name']#防止重名,加一个前缀。 with open(name,mode='wb') as f:#以bytes类型直接写进文件。 file_size = str_dic['file_size']#获取传入文件的大小。 while file_size: content = conn.recv(1024)#分次接收。 f.write(content) #接收一次写入一次。 file_size -= len(content)#剩余的大小。 conn.close()#TCP协议两个套接字。 sk.close() client: import socket import os import json import struct sk = socket.socket() sk.connect(('127.0.0.1',8080)) dic_c = {'1':'upload', '2':'download' } #定义一个供用户选择的字典 while 1: num = input('请输入序号').strip() if num == '1': for k,v in dic_c.items():#将字典展示出来。 print(k,':',v) path = input('请输入一个文件路径').strip() name = os.path.basename(path) #获取文件名,传入服务端,写入文件用。 file_size = os.path.getsize(path)#获取文件大小,用来循环并分次传输。 dic = {'opt': dic_c[num],'name':name,'file_size':file_size}#将功能、文件名、大小作为字典传入服务端。 str_dic = json.dumps(dic) #将字典序列化,变成字符串,才能传输。 len_dic = len(str_dic) #计算序列化后的字典的长度。 r = struct.pack('i',len_dic)#通过序列号后的字典的长度将其变成长度为固定4位的bytes. sk.send(r) #将固定后的bytes传入服务端。 sk.send(str_dic.encode('utf-8'))#另一端已经解出了序列化后字典的长度,发送即可。 with open(name,mode='rb') as f:#以bytes类型读取,就不用转码了。 while file_size: content = f.read(1024)#分次读取 sk.send(content) #分次传输,直到传到没有为止。另一端也是与此同样来分次接收 file_size -= len(content) break sk.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#执行系统命令:subprocess模块 # import subprocess # # res = subprocess.Popen('dir',shell=True,#告诉系统把'dir'当做系统命令来执行。 # stdout=subprocess.PIPE,#接收正确的结果 # stderr=subprocess.PIPE #接收错误的结果 # ) # # print(res.stdout.read().decode('gbk'))#windows默认编码gbk,按照gbk解码读取。 # print('stderr:'+res.stderr.read().decode('gbk'))#没有错误结果,显示空。 #黏包现象:1.发送端与接收端的数据不对等,接收端能接收的字节<发送的数据,接收到一部分数据,下次再执行命令接收的时候再从上一次 #开始。接收端能接收的字节>发送的数据,如果数据较小,nagle算法会将数据较小及时间间隔较短的数据黏包,合并到一起一同接收。 #如果没有合理的拆包机制,则造成了无法拆分,出现黏包现象。 #tcp是字节流的形式。不能有空消息,如果有空消息,流中就会出现错误。造成无法发送。需要有空消息处理机制。 #udp是数据报形式,可以有空消息。 #tcp有黏包现象,udp没有。udp是一个sendto对应一个recvfrom,一条消息对应一条消息,如果接收的字节数不够,那其余的数据 #就会丢失。所以不可靠。不会黏包。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#自己创建一个模块,通过调用此模块,省略一些编码解码的步骤。 # from socket import * # # class My_socket(socket): #以socket作为父类。 # def __init__(self,coding = 'utf-8'):#编码为默认关键字参数。 # self.coding = coding # super(My_socket, self).__init__(type=SOCK_DGRAM) #没有socket. # # def my_recv(self,num): #传进来接收的字节数。如1024 # msg_r ,addr = self.recvfrom(num) # return msg_r.decode(self.coding),addr #返回编好码的内容,按照传进来的或默认的编码方式。 # # def my_send(self,msg_s,addr): # return self.sendto(msg_s.encode(self.coding),addr)#返回编好码的要发送的内容。 server: from 练习 import My_socket sk = My_socket() #实例化一个套接字对象。 sk.bind(('127.0.0.1',8090))#正常绑定 while 1: msg_r , addr = sk.my_recv(1024) #调用类中接收方法。 print(msg_r) #不需要做解码动作。 msg_s = input('>>>') sk.my_send(msg_s,addr) #调用发送函数。不需要编码。 sk.close() client: from 练习 import My_socket sk = My_socket() while 1: msg_s = input('>>>') sk.my_send(msg_s,('127.0.0.1',8090)) msg_r ,addr = sk.my_recv(1024) print(msg_r) sk.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#编码流程: # TCP udp # s c s c # 创建套接字 创建套接字 # 绑定套接字 绑定套接字 # 监听 # 等待接收 连接 # 发送/接收 先接收/再发送 # 关闭c套接字 # 关闭s套接字 关闭s套接字 关闭套接字 关闭套接字