基于TCP的大文件发送
#server服务端 import struct import json import os import socket server = socket.socket() # 默认TCP协议 server.bind(('127.0.0.1',8080)) # 传入IP和端口 server.listen(5) # 限制半连接池的数量 while True: conn,addr = server.accept() # conn是连接,addr是地址,accept就是阻塞,等待一下,等客户端连接 while True: try: header_dict = conn.recv(4) # 接收一个字典的报头 dict_size = struct.unpack('i',header_dict)[0] # 解包得到字典的长度 dict_bytes = conn.recv(dict_size) # 接收一个字典 dict_json = json.loads(dict_bytes.decode('utf-8')) # 将字典解码并反序列化 total_size = dict_json.get('file_size') #拿出字典中真实数据的大小 recv_size = 0 with open(dict_json.get('file_name'),'wb') as f: while recv_size < total_size: data = conn.recv(1024) # 接收真实的数据 f.write(data) # 将数据写入文件 recv_size += len(data) print('上传成功') except ConnectionResetError as e: # 捕获客户端断开产生的错误 print(e) break conn.close() # 关闭客户端套接字
#client客户端 import socket import struct import json import os client = socket.socket() client.connect(('127.0.0.1',8080)) while True: MOVIE_DIR = r'D:Program Filesfeiqworkuntitledworksp' # 找到文件路径 movie_list = os.listdir(MOVIE_DIR) # 将文件夹下的文件名放到列表里 for i,movie in enumerate(movie_list,1): # 给列表中的文件名加上序号,从1开始 print(i,movie) choice = input('请输入电影序号>>>:') if choice.isdigit(): choice = int(choice) - 1 if choice in range(0,len(movie_list)): path = movie_list[choice] # 根据用户的选择,拿到文件名 file_path = os.path.join(MOVIE_DIR,path) # 拼接文件名的路径 file_size = os.path.getsize(file_path) # 获得该文件的大小 res_d = { 'file_name':path, 'file_size':file_size, 'msg':'注意身体健康' } # 将文件名,大小以及一些其他信息存放在字典里 json_d = json.dumps(res_d) # 将字典转换成json格式的数据 json_bytes = json_d.encode('utf-8') # 将这个数据变成二进制 header = struct.pack('i',len(json_bytes)) # 打包一个报头,报头的内容是字典的长度 client.send(header) # 发送一个报头 client.send(json_bytes) # 发送这个字典 with open(file_path,'rb') as f: # 找到给文件并准备发送 for line in f: client.send(line) # 循环取出数据并发送 else: print('not in range') else: print('must be a number')
UDP通信
特点:数据报协议(自带报头),没有双向通道,通信过程类似于发短信。
1.udp协议客户端允许发空
2.udp协议不会粘包
3.udp协议支持并发
***允许发空和不会粘包的原因都是因为基于数据报协议,自带了报头
UDP协议的基本使用: #服务端 import socket server = socket.socket(type=socket.SOCK_DGRAM) # UDP协议 server.bind(('127.0.0.1',8080)) #UDP不需要设置半连接池,它也没有半连接池的概念 #因为没有双向通道,不需要accept,直接就到通信循环 while True: data,addr = server.recvfrom(1024) print('数据:',data) # 客户端发来的消息 print('地址:',addr) # 客户端的地址 server.sendto(data.upper(),addr) #客户端 import socket client = socket.socket(type=socket.SOCK_DGRAM) # 不需要建立连接,直接进入通信循环 server_address = ('127.0.0.1',8080) while True: client.sendto(b'hello',server_address) data,addr = client.recvfrom(1024) print('服务端发来的数据',data) print('服务端的地址',addr)
简易版本的QQ
#服务端: import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) while True: data,addr = server.recvfrom(1024) print(data.decode('utf-8')) msg = input('>>>:') server.sendto(msg.encode('utf-8'),addr) #客户端: import socket client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: msg = input('>>>:') res = '来自客户端1的消息:%s'%msg client.sendto(res.encode('utf-8'),server_address) data,server_addr = client.recvfrom(1024) print(data.decode('utf-8'))
利用socketserver完成并发
#服务器 import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): while True: data, sock = self.request print(self.client_address) # 客户端地址 print(data.decode('utf-8')) sock.sendto(data.upper(), self.client_address) if __name__ == '__main__': """只要有客户端连接,会自动交给自定义类中的handle方法去处理""" server = socketserver.ThreadingUDPServer(('127.0.0.1',8080), MyServer) server.serve_forever() # 启动该服务对象 #客户端 import socket import time client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: client.sendto(b'hello',server_address) data,addr = client.recvfrom(1024) print(data.decode('utf-8'),addr) time.sleep(1) # 睡眠1秒,让服务端有反馈的时间
并发:看起来像同时运行的
并行:真正意义上的同时运行