网络通信 : 粘包解决的实例 socketserver模块 udp的使用 简易版QQ的实现 异常处理机制的知识点
发送大文件(粘包问题的解决)
解决思路:
客户端:
1.先获取一个文件夹下面的电影列表打印
2.选择某个电影,得到具体的文件路径
3.得到文件大小,去定义一个字典
4.制作一个字典的报头,并发送
5.发送字典
6.发送电影的文件数据
服务端:
1.先解析报头,得到字典的长度
2.接收字典的字节数据,并还原成字典
3.通过字典得到文件的长度
4.进行循环接收,写入你自己的文件中
1 #客户端: 2 import socket 3 import json 4 import os 5 import struct 6 7 8 client = socket.socket() 9 client.connect(('127.0.0.1',8080)) 10 11 while True: 12 # 获取电影列表 循环展示 13 MOVIE_DIR = r'F:\老男孩\上课内容\day01\视频' 14 movie_list = os.listdir(MOVIE_DIR) 15 # print(movie_list) 16 for i,movie in enumerate(movie_list, 1): 17 print(i, movie) 18 # 用户选择 19 choice = input('please choice movie to upload>>>:') 20 # 判断是否是数字 21 if choice.isdigit(): 22 # 将字符串数字转为int 23 choice = int(choice) - 1 24 # 判断用户选择在不在列表范围内 25 if choice in range(0,len(movie_list)): 26 # 获取到用户想上传的文件路径 27 path = movie_list[choice] 28 # 拼接文件的绝对路径 29 file_path = os.path.join(MOVIE_DIR,path) 30 # 获取文件大小 31 file_size = os.path.getsize(file_path) 32 # 定义一个字典 33 res_d = { 34 'file_name':'性感荷官在线发牌.mp4', 35 'file_size':file_size, 36 'msg':'注意身体,多喝营养快线' 37 } 38 # 序列化字典 39 json_d = json.dumps(res_d) 40 json_bytes = json_d.encode('utf-8') 41 42 # 1.先制作字典格式的报头 43 header = struct.pack('i',len(json_bytes)) 44 # 2.发送字典的报头 45 client.send(header) 46 # 3.再发字典 47 client.send(json_bytes) 48 # 4.再发文件数据(打开文件循环发送) 49 with open(file_path,'rb') as f: 50 for line in f: 51 client.send(line) 52 else: 53 print('not in range') 54 else: 55 print('must be a number') 56 #服务端: 57 import socket 58 import json 59 import struct 60 61 62 server = socket.socket() 63 server.bind(('127.0.0.1',8080)) 64 server.listen(5) 65 66 while True: 67 conn,addr = server.accept() 68 while True: 69 try: 70 header_len = conn.recv(4) 71 # 解析字典报头 72 header_len = struct.unpack('i',header_len)[0] 73 # 再接收字典数据 74 header_dic = conn.recv(header_len) 75 real_dic = json.loads(header_dic.decode('utf-8')) 76 # 获取数据长度 77 total_size = real_dic.get('file_size') 78 # 循环接收并写入文件 79 recv_size = 0 80 with open(real_dic.get('file_name'),'wb') as f: 81 while recv_size < total_size: 82 data = conn.recv(1024) 83 f.write(data) 84 recv_size += len(data) 85 print('上传成功') 86 except ConnectionResetError as e: 87 print(e) 88 break 89 conn.close()
socketserver模块
服务端(server):
1 import socketserver 2 class Myserver(socketserver.BaseRequestHandler): 3 def handle(self): 4 self.data = self.request.recv(1024).strip() 5 print("{} wrote:".format(self.client_address[0])) 6 print(self.data) 7 self.request.sendall(self.data.upper()) 8 9 if __name__ == "__main__": 10 HOST, PORT = "127.0.0.1", 9999 11 12 # 设置allow_reuse_address允许服务器重用地址 13 socketserver.TCPServer.allow_reuse_address = True 14 # 创建一个server, 将服务地址绑定到127.0.0.1:9999 15 server = socketserver.TCPServer((HOST, PORT),Myserver) 16 # 让server永远运行下去,除非强制停止程序 17 server.serve_forever()
客户端(client):
1 import socket 2 3 HOST, PORT = "127.0.0.1", 9999 4 data = "hello" 5 6 # 创建一个socket链接,SOCK_STREAM代表使用TCP协议 7 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 8 sock.connect((HOST, PORT)) # 链接到客户端 9 sock.sendall(bytes(data + "\n", "utf-8")) # 向服务端发送数据 10 received = str(sock.recv(1024), "utf-8")# 从服务端接收数据 11 12 print("Sent: {}".format(data)) 13 print("Received: {}".format(received))
UDP使用:
UDP通信:
数据报协议(自带报头)
没有双向通道 通信类似于发短信
1.udp协议客户端允许发空
2.udp协议不会粘包
3.udp协议服务端不存在的情况下,客户端发送仍不会报错
4.udp协议支持并发
基本原理:
服务端(server):
1 import socket 2 3 4 server = socket.socket(type=socket.SOCK_DGRAM) # UDP协议 5 server.bind(('127.0.0.1',8080)) 6 # UDP不需要设置半连接池 它也没有半连接池的概念 7 8 # 因为没有双向通道 不需要accept 直接就是通信循环 9 while True: 10 data, addr = server.recvfrom(1024) 11 print('数据:',data) # 客户端发来的消息 12 print('地址:',addr) # 客户端的地址 13 server.sendto(data.upper(),addr)
客户端(client):
1 client = socket.socket(type=socket.SOCK_DGRAM) 2 # 不需要建立连接 直接进入通信循环 3 server_address = ('127.0.0.1',8080) 4 while True: 5 client.sendto(b'hello',server_address) 6 data, addr = client.recvfrom(1024) 7 print('服务端发来的数据',data) 8 print('服务端的地址',addr)
基于UDP实现的简易版QQ:
服务端:
1 import socket 2 3 4 server = socket.socket(type=socket.SOCK_DGRAM) 5 server.bind(('127.0.0.1',8080)) 6 7 while True: 8 data, addr = server.recvfrom(1024) 9 print(data.decode('utf-8')) 10 msg = input('>>>:') 11 server.sendto(msg.encode('utf-8'),addr)
客户端(要设置多个):
1 import socket 2 3 4 client = socket.socket(type=socket.SOCK_DGRAM) 5 server_address = ('127.0.0.1',8080) 6 7 while True: 8 msg = input('>>>:') 9 msg = '来自客户端4的消息:%s'%msg 10 client.sendto(msg.encode('utf-8'),server_address) 11 data, server_addr = client.recvfrom(1024) 12 print(data.decode('utf-8'))
udp和tcp的区别在于:
udp没有粘包问题
异常处理机制的相关知识:
异常处理:
什么是异常?
程序在运行过程中出现了不可预知的错误
并且该错误没有对应的处理机制,那么就会以异常的形式表现出来
造成的影响就是整个程序无法再正常运行
异常的结构:
1.异常的类型:NAMEERROR
2.异常的信息:name 'fdsdfsdf' is not defined
3.异常的位置:Traceback (most recent call last):
File "D:/python脱产10期视频/day29/01 异常处理.py", line 1, in <module>fdsdfsdf
异常的种类:
分为两大类:
1.语法错误:
是你程序立刻就能解决的,这种错误是不能被容忍的
语法上的错误 发现之后应该立刻解决
2.逻辑错误:
这种错是可以被容忍的 因为一眼看不出来
针对逻辑上的错误 可以采用异常处理机制进行捕获
常见的错误类型:
NAMERROR 名字错误
SyntaxError 语法错误
KeyError 键不存在
ValueError 值错误
IndexError 索引错误
如何避免异常处理 ?
在你认为可能会出现bug的代码块上方try一下。
注意:try内部的代码块越少越好。
try:
...
可能出错的代码
except 出错的类型 as e: # 将报错信息赋值给变量e
出错之后的处理机制
...
1 # res = {'name':''} 2 # print(res['password']) 3 # print(res.get('password','fdjdsfk')) 4 5 # int('sdjajsd') 6 # l = [1,2,3,4] 7 # # l[111] 8 9 10 11 # try: 12 # name 13 # l = [1,2,3] 14 # l[111] 15 # d = {'name':'jason'} 16 # d['password'] 17 # except NameError: 18 # print('NameError') 19 # except IndexError: 20 # print('indexerror') 21 # except KeyError: 22 # print('keyerror') 23 """ 24 错误发生之后 会立刻停止代码的运行 25 执行except语句 比对错误类型 26 """ 27 28 # try: 29 # # name 30 # # l = [1,2,3] 31 # # l[111] 32 # d = {'name':'jason'} 33 # d['password'] 34 # # Exception 35 # except BaseException: # 万能异常 所有的异常类型都被捕获 36 # print('老子天下无敌') 37 38 39 40 41 42 # try: 43 # # name 44 # l = [1,2,3] 45 # l[111] 46 # # d = {'name':'jason'} 47 # # d['password'] 48 # except Exception: # 万能异常 所有的异常类型都被捕获 49 # print('老子天下无敌') 50 # else: 51 # print('被检测的代码没有任何的异常发生 才会走else') 52 # finally: 53 # print('无论被检测的代码有没有异常发生 都会在代码运行完毕之后执行我') 54 55 56 # 主动抛异常 57 # if 'egon' == 'DSB': 58 # pass 59 # else: 60 # raise TypeError('尽说大实话') 61 # 关键字raise就是主动抛出异常 62 63 64 # l = [1,2,3] 65 # assert len(l) < 0 # 断言 预言 66 # 猜某个数据的状态 猜对了 不影响代码执行 正常走 67 # 猜错了 直接报错 68 69 # 自定义异常 70 #9 自定义异常 71 class MyError(BaseException): 72 def __init__(self,msg): 73 super().__init__() 74 self.msg=msg 75 def __str__(self): 76 return '<dfsdf%ssdfsdaf>' %self.msg 77 78 raise MyError('我自己定义的异常') # 主动抛出异常其实就是将异常类的对象打印出来,会走__str__方法
并发:
看起来像同时运行的
并行:
真正意义上的同时运行