d29天 上传电影练习 UDP使用 ScketServer模块
TCP小结
socket套接字 TCP 1.最简易的版本的客户端与服务端之间通信 2.通信循环 recv() 阻塞 3.连接循环 accept() 阻塞 4.TCP粘包问题 5.struct模块 对数据进行打包处理 固定长度 pack unpack
解决粘包问题的流程
服务端 1.生成一个字典 2.制作该字典的报头 json序列化 编码 统计长度 3.发送字典的报头 4.发送字典 5.最后发真实数据
socket套接字 TCP 1.最简易的版本的客户端与服务端之间通信 2.通信循环 recv() 阻塞 3.连接循环 accept() 阻塞 4.TCP粘包问题 5.struct模块 对数据进行打包处理 固定长度 pack unpack
服务端 1.生成一个字典 2.制作该字典的报头 json序列化 编码 统计长度 3.发送字典的报头 4.发送字典 5.最后发真实数据
客户端 1.先接受固定长度的4个字节字典报头 2.解析获取字典数据的长度 unpack(...)[0] 3.接受字典数据 解码 反序列化 4.接受真实数据
为什么要多加一个字典
1.打包的数据大小有限
2.可以携带更多的信息
import socket import json import struct server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr = server.accept() while True: try: header_len = conn.recv(4) # 解析字典报头 header_len = struct.unpack('i',header_len)[0] # 再接收字典数据 header_dic = conn.recv(header_len) # 反序列化 得到真实数据 real_dic = json.loads(header_dic.decode('utf-8')) # 获取数据长度 total_size = real_dic.get('file_size') # 循环接收并写入文件 recv_size = 0 with open(real_dic.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()
import socket import json import os import struct client = socket.socket() client.connect(('127.0.0.1',8080)) while True: # 获取电影列表 循环展示 MOVIE_DIR = r'D:python脱产10期视频day25视频' movie_list = os.listdir(MOVIE_DIR) # print(movie_list) for i,movie in enumerate(movie_list,1): print(i,movie) # 用户选择 choice = input('please choice movie to upload>>>:') # 判断是否是数字 if choice.isdigit(): # 将字符串数字转为int 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':'性感荷官在线发牌.mp4', 'file_size':file_size, 'msg':'注意身体,多喝营养快线' } # 序列化字典 json_d = json.dumps(res_d) json_bytes = json_d.encode('utf-8') # 1.先制作字典格式的报头 header = struct.pack('i',len(json_bytes)) # 2.发送字典的报头 client.send(header) # 3.再发字典 client.send(json_bytes) # 4.再发文件数据(打开文件循环发送) 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')
程序在运行过程中出现了不可预知的错误
并且该错误没有对应的处理机制,那么就会以异常的形式表现出来
造成的影响就是整个程序无法再正常运
1.异常的类型:NAMEERROR 2.异常的信息:name 'fdsdfsdf' is not defined 3.异常的位置:Traceback (most recent call last): File "D:/python/day29/01 异常处理.py", line 1, in <module>
分为两大类 1.语法错误 是你程序立刻就能解决的,这种错误是不能被容忍的 语法上的错误 发现之后应该立刻解决 2.逻辑错误 这种错是可以被容忍的 因为一眼看不出来 针对逻辑上的错误 可以采用异常处理机制进行捕获
NAMERROR 名字错误
SyntaxError 语法错误
KeyError 键不存在
ValueError 值错误
IndexError 索引错误
TypeError 类型错误
异常处理
在你认为可能会出现bug的代码块上方try一下:注意try内部的代码块越少越好
try:
可能出错的代码
except 出错的类型 as e: # 将报错信息赋值给变量e
出错之后的处理机制
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection 参考模型中一种无连接的传输层协议,提供简单不可靠信息传送服务
是一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成。且不对传送数据包进行可靠性保证,适合于一次传输少量数据
import socket client = socket.socket(type=socket.SOCK_DGRAM) # 不需要建立双向连接 直接收发数据 server_addres = ('127.0.0.1',8080) # 发送数据需要对方的大致 while True: client.sendto(b'strawberry',server_addres) data , addr = client.recvfrom(1024) print('服务端发的数据',data) print('服务端发的地址',addr)
import socket # UDP中要指定type=socket.SOCK_DGRAM,默认代表的是TCP server = socket.socket(type=socket.SOCK_DGRAM) 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) #发送数据需要客户端的地址
tcp 和udp tcp:可靠,传输安全,粘包 通过连接传输:在发送数据时,会等到对方确定接收完成时,将数据删除,如果没有,就会保存到内存,等待确认 udp:不可靠,不须建立连接,不粘包, 发送数据,一旦发送,就会删除缓存数据,如果没收到,那就没收到 与TCP的区别 ***** 不可靠传输 不需要建立连接 不会粘包 单次数据包不能太大
服务器端
服务器不需要监听 listen
不需要接收请求 accept
收数据 recvfrom(缓冲区大小)
发数据 sendto(数据,地址)
客户端:
不需要建立连接
收数据 recvfrom(缓冲区大小)
发数据 sendto(数据,地址)
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) data, addr1 = server.recvfrom(1024) print(data) data, addr2 = server.recvfrom(1024) print(data)
import socket client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) # while True: # msg = input('>>>:') client.sendto(b'hello',server_address) client.sendto(b'hello',server_address) client.sendto(b'hello',server_address) # data, server_addr = client.recvfrom(1024) # print(data
实现多个用户同时发信息(实现并发)
并发:看起来像同时运行的
并行:真正意义上的同时运行
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('>>>:') msg = '来自客户端1的消息:%s'%msg client.sendto(msg.encode('utf-8'),server_address) data, server_addr = client.recvfrom(1024) print(data.decode('utf-8'))
import socket client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: msg = input('>>>:') msg = '来自客户端2的消息:%s'%msg client.sendto(msg.encode('utf-8'),server_address) data, server_addr = client.recvfrom(1024) print(data.decode('utf-8'))
import socket client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: msg = input('>>>:') msg = '来自客户端3的消息:%s'%msg client.sendto(msg.encode('utf-8'),server_address) data, server_addr = client.recvfrom(1024) print(data.decode('utf-8'))
socketserver模块是基于socket而来的模块,它是在socket的基础上进行了一层封装,并且实现并发等功能。
1、创建一个继承socketserver.BaseRequestHandler的类 2、类中必须重写一个名为handler的方法 3、实例化一个服务器类,传入服务器的地址和请求处理程序类 4、调用serve_forever()事件循环监听
代码
#!/usr/bin/env python3 import socketserver class Handler(socketserver.BaseRequestHandler): # 必须继承BaseRequestHandler def handle(self): # 必须有handle方法 print('New connection:',self.client_address) while True: data = self.request.recv(1024) if not data:break print('Client data:',data.decode()) self.request.send(data) if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1',8009),Handler) # 实例化对象,实现多线程的socket server.serve_forever() # 事件监听,并调用handler方法
让tcp也能够实现udp能够看起来多个客户在运行
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): # print('来啦 老弟') while True: data = self.request.recv(1024) print(self.client_address) # 客户端地址 print(data.decode('utf-8')) self.request.send(data.upper()) if __name__ == '__main__': """只要有客户端连接 会自动交给自定义类中的handle方法去处理""" server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) # 创建一个基于TCP的对象 server.serve_forever() # 启动该服务对象
server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) # 创建一个基于TCP的对象 ThreadingTCPServer多线程tcpserver 会帮你实时监听代码中的地址,一旦有用户来进行请求,它会将这个用户交给MyServer类中的handl方法来处理,会自动触发方法.
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: client.send(b'hello') data = client.recv(1024) print(data.decode('utf-8'))
开udp 支持并发
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): # print('来啦 老弟') 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) # 创建一个基于TCP的对象 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)