面向对象的使用
#使用面向对象解决datetime.tody()无法序列化的问题
import json
from datetime import datetime,date
# print(date.today()) #年月日 2020-12-08
# print(datetime.today()) #年月日时分秒 2020-12-08 16:07:34.919540
class Myjson (json.JSONEncoder):
def default(self, o):
if isinstance(o,datetime):
return o.strftime('%Y-%m-%d %X')
elif isinstance(o,date):
return o.strftime('%Y-%m-%d')
else:
return super().default(self,o)
res = {'c1':datetime.today(),'c2':date.today()}
print(json.dumps(res,cls=Myjson))
网络编程之 TCP
#学习网络编程可以开发一个cs架构的软件
#学习并发、数据库、前端、django可以开发bs架构的软件
#软件开发架构
C/S架构(client/server)
C:客户端,APP
S:服务端,架构
B/S架构(browser/server)
B:浏览器,taobao.com/jd.com
S:服务端
bs架构实际上也是cs架构
统一接口:比如微信、支付宝
#服务端
24小时不间断提供服务
#客户端
什么时候想体验服务,什么时候去找心仪的服务端寻求服务
#网络编程发展史
任何先进的技术基本上都是来源于军事
#回到过去
1.早期的电话 电话线
2.大屁股电脑 网线
3.笔记本电脑,无线电话 网卡
#要想实现远程通信,第一个需要具备的条件是:物理连接介质
人想要实现交流必须统一语言 >>> 英文
计算机想要与计算机远程通信,除了有物理连接介质之外,还需要有一套公共的标准和协议
#OSI协议
OSI七层协议
应用层
HTTP协议:超文本传输协议
HTTPS协议
FTP协议
表示层
会话层
传输层
TCP协议/UDP协议,都是基于端口工作的协议
TCP:流式协议、可靠协议(反馈机制),慢,打电话
基于TCP协议通信,必须先建立双向通道:TCP协议的三次握手、四次挥手
三次握手:
洪水攻击,一台服务器在短时间内接收到了大量的请求
四次挥手:
time_wait
UDP:数据报协议,不可靠的协议,快,发短信,QQ
无序建立双向通道,数据的传输不安全
端口:用来唯一标识计算机上的某个应用程序(0~65535),统常0~1024这些都是操作系统默认使用的端口号,建议手动指定8000以后的端口号(访问端口,输出端口)
mysql 3306
redis 6379
djando 8000
flask 5000
tomcat 8080
IP+端口:唯一标识接入互联网的一台计算机上的某个应用程序
域名解析:URL DNS
网络层
IP协议,规定了只要是接入互联网的计算机都必须有一个IP地址(公网、私网)
IP地址的特点:点分十进制(0.0.0.0 255.255.255.255)
IP地址目前有两个版本:IPV4、IPV6
路由器:实现局域网与局域网之间的互连
交换机:功能强大的路由器,让连接了交换机的计算机,实现彼此之间的互连
局域网:是构成互联网的基本单位
数据链路层
1.规定电信号的分组方式
2.规定了任何一台接入互联网的计算机,都必须有一块网卡,该网卡上有世界上独一无二的编号(mac地址),该编号由12位16进制数组成(前六位是厂商编号,后六位是流水线编号)
#1、2统称为以太网协议(通信基本靠吼)(局域网内使用)
1.广播风暴(广播、单播)
#ARP协议:根据ip地址,获取Mac地址,本地缓存
物理连接层
基于电信号,传输0100010001这样的二进制数据
OSI五层协议
应用层
传输层
网络层
数据链路层
物理连接层
#应用程序所需要的的数据,都是跟程序所在的那台计算机的内存去要
#TCP协议之所以可靠的原因在于反馈机制
反馈机制:计算机每次发数据的时候,必须等到对方的才会将内存中的数据清除,否则会在一定的时间内,每隔一段时间发送一次
socket(套接字)
Ji
#server
import socket
server = socket.socket() #不传参数,默认使用的就是TCP协议,买手机
server.bind(('127.0.0.1',8080)) #bind内为元组,127.0.0.1,为本地回环地址,插电话卡
server.listen(5) #半连接池,开机
conn,addr = server.accept() #接听电话,一直等待(阻塞)
data = conn.recv(1024) #听别人说话,接收1024个字节,阻塞
print(data)
conn.send(b'hello baby') #给别人回话
conn.close() #挂电话
server.close() #关机
#client
import socket
client = socket.socket() #拿电话
client.connect(('127.0.0.1',8080)) #拨号,写对方的ip和端口
client.send(b'hello world') #对别人说话
data = client.recv(1024) #听别人说话,阻塞
print(data)
client.close() #挂电话
#send()与recv()不要出现两边相同的情况
#recv()是跟内存要数据,至于数据的来源,recv不管
循环通信
#server
import socket
server = socket.socket() #生成一个对象
server.bind(('127.0.0.1',8080)) #绑定ip和端口
server.listen(5) #半连接池
conn,addr = server.accept() #阻塞
while True:
data = conn.recv(1024) #听别人说话,接收1024个字节
print(data)
conn.send(data.upper()) #给别人回话
# conn.close() #挂电话
# server.close() #关机
#client
import socket
client = socket.socket() #生成一个对象
client.connect(('127.0.0.1',8080))
while True:
msg = input('请输入要发送的内容>>>: ').encode('utf-8')
client.send(msg) #对别人说话
data = client.recv(1024) #听别人说话
print(data)
# client.close()
socket通信的一些问题
#server
import socket
server = socket.socket() #生成一个对象
server.bind(('127.0.0.1',8080)) #绑定ip和端口
server.listen(5) #半连接池
conn,addr = server.accept() #阻塞,addr就是客户端的地址
while True:
try:
data = conn.recv(1024) #conn()类似于双向通道
print(data) #Mac和linux客户端异常退出之后,服务端不会报错,会一致接收b''
if len(data) == 0:break
conn.send(data.upper()) #给别人回话
except ConnectionResetError as e:
print(e)
break
conn.close() #挂电话
server.close() #关机
#client
import socket
client = socket.socket() #生成一个对象
client.connect(('127.0.0.1',8080))
while True:
msg = input('请输入要发送的内容>>>: ').encode('utf-8')
if len(msg) == 0:continue #解决客户端输入空,resc()同时出现的情况
client.send(msg) #对别人说话
data = client.recv(1024) #听别人说话
print(data)
# client.close()
连接循环
#服务端
固定的ip和端口
24小时不间断提供服务
#server
import socket
server = socket.socket() #生成一个对象
server.bind(('127.0.0.1',8080)) #绑定ip和端口
server.listen(5) #半连接池,允许最大的等待数(连接数为6)
while True:
conn,addr = server.accept() #阻塞,addr就是客户端的地址
while True:
try:
data = conn.recv(1024) #conn()类似于双向通道
print(data) #Mac和linux客户端异常退出之后,服务端不会报错,会一致接收b''
if len(data) == 0:break
conn.send(data.upper()) #给别人回话
except ConnectionResetError as e:
print(e)
break
conn.close() #挂电话
# server.close() #关机
#client
import socket
client = socket.socket() #生成一个对象
client.connect(('127.0.0.1',8080))
while True:
msg = input('请输入要发送的内容>>>: ').encode('utf-8')
if len(msg) == 0:continue #解决客户端输入空,resc()同时出现的情况
client.send(msg) #对别人说话
data = client.recv(1024) #听别人说话
print(data)
# client.close()
模拟终端
#subprocess模块
import subprocess
while True:
cmd = input('请输入您的命令>>>: ')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
print(obj.stdout.read().decode('gbk')) #正确命令返回结果,windows默认使用gbk编码
print(obj.stderr.read().decode('gbk')) #错误命令返回结果
TCP协议的粘包问题
#TCP协议的特点
会将数据量比较小的,并且时间间隔比较短的数据一次性打包发给对方
#server
import socket
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
conn,addr = server.accept()
data = conn.recv(5)
print(data)
data = conn.recv(5)
print(data)
data = conn.recv(4)
print(data)
conn.send(b'hello baby')
conn.close()
server.close()
#client
import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
client.send(b'hello')
client.send(b'world')
client.send(b'baby')
data = client.recv(8)
print(data)
client.close()
#少的数据量将会一次打包发送给服务端,节省资源
#如果知道数据的发送方发送的字节数,那么双方的信息就可以不用在担心'字节长度问题'
struct模块
#struct模块
可以将任意字符串打包成字节长度为4的包,解包后仍可以得到原来字符串的长度
当原始数据特别大的时候,i模式装不下了,这个时候就需要更换模式
import struct
str = 'evfreatteg.t.g..t;hyh;y;5;yjjjjjn'
print('原始的: ',len(str))
str1 = struct.pack('i',len(str)) #将数据打包
print(len(str1)) #包长度固定位4
str2 = struct.unpack('i',str1)[0] #将数据解包
print('解包后的: ',str2)
原始的: 33
4
解包后的: 33
使用struct模块解决粘包问题
#解决粘包问题
服务端:
1.先制作一个要发送给客户端的字典
2.制作字典的报头
3.发送字典的报头
4.发送字典
5.再发送真实数据长度
客户端:
1.先接受字典的报头
2.解析拿到字典的数据长度
3.接收字典
4.从字典中获取真实数据长度
5.接收真实数据
#server
import socket
import subprocess
import struct
import json
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr = server.accept()
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0:break
cmd = cmd.decode('utf-8')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
res = obj.stdout.read() + obj.stderr.read()
d = {'name':'syy','file_size':len(res),'info':'大家为我骄傲'}
json_d = json.dumps(d)
#1.先制作一个字典的报头
header = struct.pack('i',len(json_d))
#2.发送字典报头
conn.send(header)
#3.发送字典
conn.send(json_d.encode('utf-8'))
#4.再发送真实数据
conn.send(res)
# conn.send(obj.stdout.read()) #只能read()一次
# conn.send(obj.stderr.read())
except ConnectionResetError as e:
print(e)
break
conn.close()
# server.close() #关机
#client
import socket
import struct
import json
client = socket.socket() #生成一个对象
client.connect(('127.0.0.1',8080))
while True:
msg = input('请输入要发送的内容>>>: ').encode('utf-8')
if len(msg) == 0:continue #解决客户端输入空,resc()同时出现的情况
client.send(msg) #对别人说话
#1.先接收字典报头
header_dict = client.recv(4)
#2.解析报头,获取字典长度
dict_size = struct.unpack('i',header_dict)[0]
#3.循环接收字典数据
dict_bytes = client.recv(dict_size)
dict_json = json.loads(dict_bytes.decode('utf-8'))
#4.从字典中获取信息
print(dict_json)
recv_size = 0
real_data = b''
while recv_size < dict_json.get('file_size'):
data = client.recv(1024)
real_data += data
recv_size += len(data)
print(real_data.decode('gbk'))
# client.close()
练习
#习题
写一个上传电影的功能
1.循环打印某一个文件夹下面的所有的文件
2.用户想要上传的文件
3.将用户选择的文件上传到服务器
4.服务端保存该文件
#server
import socket
import subprocess
import struct
import json
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()
# server.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:python视频day29视频'
movie_list = os.listdir(MOVIE_DIR)
for i,movie in enumerate(movie_list,1):
print(i,movie)
#用户选择
choise = input('请输入电影序号>>>: ').strip()
#判断是否是数字字符串
if choise.isdigit():
choise = int(choise)
#判断用户输入的值的范围是否有效
if choise in range(1,len(movie_list)+1):
#获取到用户想要上传的文件名
path = movie_list[choise-1]
#拼接文件的绝对路径
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_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('请输入数字序号!')
异常处理
#什么是异常
程序在运行过程中,出现不可预知的错误
并且该错误没有对应的处理机制,那么就会以异常的形式表现出来
造成的影响是整个程序无法正常运行
#异常的结构
1.异常的类型 NAMEERROP
2.异常的信息 name 'hhhhhhh' is not defined
3.异常的位置 File "E:/python_test/a.py", line 6, in <module>
#异常的种类
1.语法错误
是程序员可以立刻解决的,这种错误是不能容忍的
2.逻辑错误
这种错误是可以被容忍的,因为一眼看不出来
针对逻辑上的错误,可以采用异常处理机制进行捕获
#常见的错误类型
1.名字错误 NAMEERROP,变量名、函数名、类名不存在
2.语法错误 SyntaxError,if/while
3.键不存在 KeyError
d = {'name':'syy'}
print(d['password'])
4.值错误 ValueError
int('ssss')
5.索引错误 IndexError
l = [1,2,3]
print(l[111])
6.关键字assert断言错误 AssertionError
#异常处理
在你认为可能会出现bug的代码块上方try一下(try代码块越少越好)
#异常处理的格式
try:
想要监控的代码块
except 错误类型:
监控到错误类型后自定义的反馈
try:
想要监控的代码块
except 错误类型 as e: #e代表本来代码的错误反馈
监控到错误类型后自定义的反馈
try:
想要监控的代码块
except 错误类型:
监控到错误类型后自定义的反馈
else:
print('被检测的代码没有任何异常发生,才会走else')
finally:
print('被检测的代码无论有没有异常发生,都会走finally')
#例
try:
name
except NameError:
print('NameError已被我处理')
#except可以跟多个
try:
# name
# l = [1,2,3]
# l[100]
# d = {'name':'syy'}
# d['password']
# if #语法错误不能被监控
int('www')
except NameError:
print('NameError已被我处理')
except IndexError:
print('IndexError已被我处理')
except KeyError:
print('KeyError已被我处理')
except SyntaxError:
print('SyntaxError已被我处理')
except ValueError:
print('ValueError已被我处理')
#万能异常捕获
try:
# name
# l = [1,2,3]
# l[100]
# d = {'name':'syy'}
# d['password']
# if
int('www')
except Exception: #万能异常捕获,继承自BaseException
print('所有异常已被我处理')
#else语法
try:
# name
# l = [1,2,3]
# l[100]
# d = {'name':'syy'}
# d['password']
# if
int('1')
except Exception:
print('所有异常已被我处理')
else:
print('被检测的代码没有任何异常发生,才会走else')
finally:
print('被检测的代码无论有没有异常发生,都会走finally')
抛出异常
#关键字raise,就是主动抛出异常
if 'syy' == 'cool':
pass
else:
raise NameError('我觉得不对...')
断言错误
#关键字assert
在关键字assert预言的位置,如果语言对了,代码正常执行,错了的话,报AssertionError错误
l = [1,2,3]
assert len(l) > 0
print('断言对了才会走这行代码')
自定义异常
#主动抛出异常,其实就是将异常类的对象打印出来,会走__str__方法(对象的属性被打印的时候触发)
class MyError(BaseException):
def __init__(self,msg):
super().__init__()
self.msg = msg
def __str__(self):
return '<%shhhhhh>' %self.msg
raise MyError('自定义异常')
网络编程之 UDP
UDP通信的基本使用
#UDP通信
数据包协议自带报头
基于UDP协议的数据传输,数据是不安全的(客户端只发,无论服务端有没有接收)
#server
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)
#client
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
#不需要建立连接,直接进入通讯循环
server_address = ('127.0.0.1',8080)
while True:
client.sendto(b'hellow',server_address)
data,addr = client.recvfrom(1024)
print('服务端发来的数据',data)
print('服务端的地址',addr)
#UDP类似于发短信,TCP类似于打电话
UDP协议与TCP协议的异同
#TCP协议
1.TCP协议客户端允许为空
2.TCP协议存在粘包问题
3.udp协议服务端不存在的情况下,客户端照样会报错
4.TCP协议不支持并发,使用模块可以做到并发
#UDP协议
1.udp协议客户端允许为空? 是的
2.udp协议不会粘包? 会的
3.udp协议服务端不存在的情况下,客户端照样不会报错? 会报错,client.recvfrom(1024)报错
4.udp协议支持并发 是的
#并发:看起来像是同时运行
#并行:真正意义上的同时运行
基于UDP协议实现简易版的QQ
#server
import socket
server = socket.socket(type=socket.SOCK_DGRAM) #UDP协议
server.bind(('127.0.0.1',8080))
while True:
data,addr = server.recvfrom(1024)
print(data.decode('utf-8')) #客户端发来的消息
msg = input('请输入你要发送的内容>>>: ').strip()
server.sendto(msg.encode('utf-8'),addr)
#client
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
while True:
msg = input('请输入您要发送的内容(客户端1)>>>: ').strip()
client.sendto(msg.encode('utf-8'),server_address)
data,addr = client.recvfrom(1024)
print(data.decode('utf-8'))
socketserver模块
模块的简单使用
#server
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print('来了?老弟')
#创建一个基于TCP的对象
#只要有客户端连接,会自动交给自定义类中的handler方法去处理
server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer)
#启动该服务的对象
server.serve_forever()
#client
import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
socketserver模块使TCP协议支持并发
#server
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())
#村赶紧啊一个基于TCP的对象
server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer)
#启动该服务的对象
server.serve_forever()
#client
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'))
socketserver模块与UDP协议
#server
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)
#村赶紧啊一个基于TCP的对象
server = socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyServer)
#启动该服务的对象
server.serve_forever()
#client
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)