TCP小结

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
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection 参考模型中一种无连接的传输层协议,提供简单不可靠信息传送服务
是一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成。且不对传送数据包进行可靠性保证,适合于一次传输少量数据
UDP使用

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:可靠,传输安全,粘包 通过连接传输:在发送数据时,会等到对方确定接收完成时,将数据删除,如果没有,就会保存到内存,等待确认 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
UDP实现简易版本的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('>>>:') 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模块
socketserver模块是基于socket而来的模块,它是在socket的基础上进行了一层封装,并且实现并发等功能。
ThreadingTCPServer的使用方法
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)