JsonP
// 利用script的src属性绕过浏览器的同源策略,但是只能发GET请求 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="button" onclick="Jsonp1();" value='提交'/> </p> <p> <input type="button" onclick="Jsonp2();" value='提交'/> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function Jsonp1(){ var tag = document.createElement('script'); tag.src = "http://c2.com:8000/test/"; document.head.appendChild(tag); document.head.removeChild(tag); } function list(data){ console.log(data) } function Jsonp2(){ $.ajax({ url: "http://c2.com:8000/test/", type: 'GET', dataType: 'JSONP', jsonp:'callback', //URL上多出 ?callback=list jsonpCallback:'list', //返回数据后执行list函数 // success: function(data, statusText, xmlHttpRequest){ // console.log(data); // 没有jsonp,jsonpCallback参数,执行此函数。 } }) } </script> </body> </html> //远程: //func_name = request.GET.get('callback') //return HttpResponse('%s("机密数据")' %func_name)
CORS
Access-Control-Allow-Origin Access-Control-Request-Method Access-Control-Request-Headers
乐观锁
应用于抢票,排号系统 乐观锁,其实是开发者自己实现的逻辑, 如果更新时的数据库数据和读取时不一样,则等会再重试 更新失败后,休息一段时间后再进行重试
Python内存管理机制&垃圾回收机制
两个重要的结构体
include/object.h
#define _PyObject_HEAD_EXTRA struct _object *_ob_next; // 双端链表的下一个指针 struct _object *_ob_prev; // 双端链表的上一个指针 #define PyObject_HEAD PyObject ob_base; #define PyObject_VAR_HEAD PyVarObject ob_base; typedef struct _object { _PyObject_HEAD_EXTRA // 用于构造双向链表 Py_ssize_t ob_refcnt; // 引用计数器 struct _typeobject *ob_type; // 数据类型 } PyObject; typedef struct { PyObject ob_base; // PyObject对象 Py_ssize_t ob_size; // 元素个数 } PyVarObject;
以上源码是Python内存管理中的基石,其中包含了:
- 2个结构体
- PyO bject,此结构体中包含3个元素。
- _PyObject_HEAD_EXTRA,用于构造双向链表。
- ob_refcnt,引用计数器。
- *ob_type,数据类型。
- PyVarObject,次结构体中包含4个元素(ob_base中包含3个元素)
- ob_base,PyObject结构体对象,即:包含PyObject结构体中的三个元素。
- ob_size,内部元素个数。
- PyO bject,此结构体中包含3个元素。
- 3个宏定义
- PyObject_HEAD,代指PyObject结构体。
- PyVarObject_HEAD,代指PyVarObject对象。
- _PyObject_HEAD_EXTRA,代指前后指针,用于构造双向队列。
Python中所有类型创建对象时,底层都是与PyObject和PyVarObject结构体实现,一般情况下由单个元素组成对象内部会使用PyObject结构体(float)、由多个元素组成的对象内部会使用PyVarObject结构体(str/int/list/dict/tuple/set/自定义类),因为由多个元素组成的话是需要为其维护一个 ob_size(内部元素个数)。
1. python是由C开发. 2. include / objects 3. 在Python中所有东西创建对象时候,内部都会存储一个数据. // 维护双向链表 struct _object *_ob_next; struct _object *_ob_prev; // 引用计数器 Py_ssize_t ob_refcnt; // 类型 struct _typeobject *ob_type; 如果是由多个元素组成的话,内部会再多维护一个 Py_ssize_t ob_size; // 元素个数 4. 在创建对象时,如: 操作: v = 0.3 源码内部: a. 开辟内存. b. 初始化 ob_fval=0.3 ob_type=float ob_refcnt=1 c. 将对象加入到双向链表中 ref_chain 操作: name = v 源码内部: ob_refcnt+1 操作: del v 源码内部: ob_refcnt-1 操作: def fun(arg): print(123) fun(name) 源码内部: 刚进去:ob_refcnt+1 执行完:ob_refcnt-1 操作: del name 源码内部: ob_refcnt-1 每次应用计数器减一时,都会检查是否以为0, 如果是0,则认为他是垃圾,就对他进行回收. 5. Python内部为了提升效率,会做一些缓存机制.
#define _PyObject_HEAD_EXTRA struct _object *_ob_next; struct _object *_ob_prev; typedef struct _object { // 维护双向链表 _PyObject_HEAD_EXTRA // 引用计数器 Py_ssize_t ob_refcnt; // 类型 struct _typeobject *ob_type; } PyObject; typedef struct { // 4个: 类型/引用计数器/维护双向链表 PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject; #define PyObject_HEAD PyObject ob_base; #define PyObject_VAR_HEAD PyVarObject ob_base; 使用的类: Pyobject: /float/ PyVarObject: list/dict/set/tuple/str/int/bool
内存管理机制
Python是由C语言开发,操作都是基于底层C语言实现. Python中创建每个对象,内部都会与C语言结构体维护一些值. PyObject 上 下 计数器 类型 PyVarObject PyObject 容量个数 在创建对象时,每个对象至少内部有4个值:双向链表/ob_refcnt/ob_type.,之后会对内存中的数据进行初始化,初始化本质: 引用计数器=1,赋值. 然后将对象添加到双向链表中refchain. 以后再有其他变量执行这个内存,则让引用计数器+1,如果销毁某个变量,则找到指向的内存,将其引用计数器-1. 引用计数器如果为0,则进行垃圾回收. 在内部可能存在缓存机制,例如:float/list/int , 最开始不会真正销毁,而是放在free_list的链表中,以后再创建同类型的数据时,会先去链表中取出对象,然后再对对象进行初始化.(float:100/list:80)
垃圾回收机制
引用计数器为主,标记清除和分代回收为辅. - 引用计数器 (同上) - 引用计数器会出现循环引用 a = [1,2] b = [5,6] a.append(b) # b的计数器2 b.append(a) # a的计数器2 del a del b - 标记清楚,针对那些容器类的对象,在Python中会将他们单独放到一个双向量表中,做定期扫描,检查是否有循环引用,如果有则各自-1,如果-1之后等于0,则直接回收. - 分代回收,为了少扫描对象,将没有问题的对象让他放上上一级链表中,默认下一级扫10次,上一代才扫描1次,总共有3代.
带参数的装饰器
from functools import wraps def auto(arg): def wapper(func): @wraps(func) # 不改变被装饰函数的__name__,__doc__等 def inner(*args, **kwargs): print(arg) ret = func(*args, **kwargs) return ret return inner return wapper @auto('怀心抱素') def func(arg): print(func.__name__) return arg print(func('哈哈'))
显示目录下的所有文件
import os print(list(os.walk('path')))
获取黑窗口的参数
import sys a = sys.argv
上下文管理
class A: def __init__(self, path, mode='r', encoding='utf-8'): self.open = open(path, mode=mode, encoding=encoding) def __enter__(self): return self.open def __exit__(self, exc_type, exc_val, exc_tb): self.open.close() with A(r'D:\python23期\testdjango\templates\test.html', mode='r') as f1: for line in f1: print(line)
值类型/引用类型
值类型:int/str/bool 引用类型:list/dict/tuple/set(容器类)
构造函数/析构函数
class Foo: __new__ __del__
__new__
返回值是什么?
class Foo: def __init__(self,name): self.name = name def __new__(cls,*arg,**kwargs): # 返回的是一个开辟了空间的对象,但是还没有初始化封装属性 return object.__new__(cls) def __del__(self): pass obj = Foo('alex') del obj # 执行析构方法
单例模式
# 第一种: 线程安全的单例模式 from threading import Lock class A: __instance = None __lock = Lock() def __init__(self): self.name = '单例模式' def __new__(cls, *args, **kwargs): with cls.__lock: if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance # 第二种: 在一个py文件中实例化类,在其他的py文件中引用,由于加载过的脚本不会再加载,所以也是单例模式
重写
class Base: def f1(self): pass class Foo(Base): # 子类重写父类方法 def f1(self): pass
重载(Python中没有)
# 在C#/JAVA中允许重名方法,只要参数不同即可 class Base: def f1(self): pass def f1(self,a1): print(a1)
接口
# 第一种: API # 第二种:Java/C#, 约束实现它的类必须实现指定方法. interface IMessage: def f1(self):pass 接口用interface表关键字修饰,内部可以有方法,方法中不能写代码. class Foo(IMessage): def f1(self): print(123) Foo类"实现"接口IMessage,必须内部实现接口中定义的方法.就像python的类的约束
Python中的约束: 异常+继承
class Base(object): def func(self): raise NotImplementedError('子类必须实现func方法') class Foo(Base): pass obj = Foo() obj.func() # 软约束,调用方法时才会报错
Python中抽象类 + 抽象方法
# 很少用 import abc class Base(metaclass=abc.ABCMeta): @abc.abstractmethod def func(self): pass def data(self): print(123) class Foo(Base): pass obj = Foo() # 硬约束,实例化就报错
响应式布局
# 根据不同的设备,显示不同的样式,做到自适应 <style> #box { height: 50px; background-color: red; } @media (max-width: 700px) { {# 宽度小于700使用这里的样式 #} #box { background-color: yellow; } } </style>
HTTP协议
超文本传输协议,基于TCP协议,规定了浏览器和服务器的通信格式 本质是一个规范和约束 一次请求&一次响应就断开连接: 短链接/无状态 数据格式: 请求行/响应行 请求头/响应头 空行 请求体/响应体 默认端口: 80 数据明文传输
HTTPS协议
安全的数据传输的过程 需要三个角色: 服务器(公司),客户端(个人),证书管理机构 1.公司向证书管理机构申请证书(包含公司信息,公钥,签名)和私钥 2.公司保管私钥 3.当客户端向服务器发起请求时,服务器将证书发给客户端 4.客户端会验证证书的有效性(通过签名) 5.验证通过后,在本地生成一个密钥,使用证书内的公钥对密码进行加密,然后发给服务器 6.服务器拿到客户端发来的加密数据,使用私钥解密出客户端的密钥 7.这样客户端和服务器就有了一对对称密钥,然后使用这对密钥进行通讯
轮询
用一个web聊天室来举例 前端定时向后端频繁发送请求,询问是否有新的消息 - 压力大 - 延迟 <script> function getMessage() { $.ajax({...}) } setInterval(getMessage,2000) </script>
长轮询
使用队列Queue的阻塞机制,在前端请求数据时,如果没有信息,则让他阻塞一段时间,一有信息马上返回,前端收到响应后,马上再次发送请求 view中: from queue import Queue from django.http.response import JsonResponse user_dict = {} def func(request): try: user = request.GET.get('user') message = user_dict[user].get(timeout=30) ret = { 'status': True, 'data': message } except Exception: ret = { 'status': False } return JsonResponse(ret) class TestView(View): def get(self, request): user_name = request.GET.get('user') user_dict[user_name] = Queue() return render(request, 'test.html', {'user': user_name}) def post(self, request): message = request.POST.get('data') user = request.POST.get('user') print(message) print(user) for i in user_dict: user_dict[i].put(message) return HttpResponse('ok') <script> function postMessage() { $.ajax({ url: '/test/', type: 'POST', data: { "data": $('#data').val(), user: "{{ user }}" }, success: function (res) { } }) } function getMessage() { $.ajax({ url: '/func/', type: 'GET', data: { user: "{{ user }}" }, success: function (res) { console.log(res); if (res.status) { var p = document.createElement('p'); p.innerText = res.data; $('#message').append($(p)) } getMessage() } }) } getMessage() </script>
websocket
实现了web版的socket,浏览器会创建一个socket,并且与服务端创建连接,连接后不断开. 重点: 魔法字符串magicstring 原理: 1.浏览器发起websocket请求,浏览器生成一个随机的字符串 2.将随机字符串发给服务端(这次发的是HTTP请求),服务端将魔法字符串和随机字符串(固定的)进行拼接,然后对拼接的结果进行加密(sha265),将加密结果返回给客户端 3.客户端收到响应,在本地也将魔法字符串和随机字符串进行拼接和加密,然后和响应进行比较,如果比较成功,意味服务端支持websocket,这样就完成了校验服务端是否支持websocket(这个验证过程称为握手环节) 4.客户端向服务端发送信息(加密的) 5.服务端收到客户端的加密信息进行解密(统一的) 1.取出第二个字节的后7位(即10-16位),最大表示0-127 2.对这个值进行判断(当前位置在第二个字符(16位)) <= 125 前2个字节是数据头,后面就是数据包 == 126 从当前位置再往后读2个字节,后面的是数据包 == 127 从当前位置再往后读8个字节,后面的是数据包 3.拿到数据包进行解密 前四个字节是masking key, 后面的是加密的数据 对数据进行解密,一个字节一个字节的解密 拿一个字节,第一个是0,和0%4取余,得到的值作为索引,取出masking key的索引对应的字节,和这个字节做与运算,得到一个字节的真正数据,然后读第二个字节,直至完毕,就拿到了全部的解密数据
import socket import base64 import hashlib def get_headers(data): """ 将请求头格式化成字典 """ header_dict = {} data = str(data, encoding='utf-8') header, body = data.split('\r\n\r\n', 1) header_list = header.split('\r\n') for i in range(0, len(header_list)): if i == 0: if len(header_list[i].split(' ')) == 3: header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ') else: k, v = header_list[i].split(':', 1) header_dict[k] = v.strip() return header_dict def get_real_data(info): """ 获取解密数据 & 与运算 例如 11101010 01111111 01101010 """ payload_len = info[1] & 127 if payload_len == 126: extend_payload_len = info[2:4] mask = info[4:8] decoded = info[8:] elif payload_len == 127: extend_payload_len = info[2:10] mask = info[10:14] decoded = info[14:] else: extend_payload_len = None mask = info[2:6] decoded = info[6:] bytes_list = bytearray() for i in range(len(decoded)): chunk = decoded[i] ^ mask[i % 4] bytes_list.append(chunk) body = str(bytes_list, encoding='utf-8') return body sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 8002)) sock.listen(5) # 等待用户连接 conn, address = sock.accept() data = conn.recv(1024) headers = get_headers(data) # 提取请求头信息 # 对请求头中的sec-websocket-key进行加密 response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \ "Upgrade:websocket\r\n" \ "Connection: Upgrade\r\n" \ "Sec-WebSocket-Accept: %s\r\n" \ "WebSocket-Location: ws://%s%s\r\n\r\n" magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' value = headers['Sec-WebSocket-Key'] + magic_string ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url']) # 响应【握手】信息 print(headers['Sec-WebSocket-Key']) conn.send(bytes(response_str, encoding='utf-8')) while True: data = conn.recv(1024) print(get_real_data(data))
Django实现websocket
# 使用channels(官方推荐) python3.6 (3.5和3.7会报错) # 内部依赖asgi协议, daphne(支持websocket/http协议)
- channel 实现基本 websocket
- 给多个人发送消息使用 channel layer
from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer from asgiref.sync import async_to_sync class ChatConsumer(WebsocketConsumer): def websocket_connect(self, message): """建立连接触发""" self.accept() async_to_sync(self.channel_layer.group_add)('12312312', self.channel_name) def websocket_receive(self, message): """有消息自动触发""" text_data = message['text'] async_to_sync(self.channel_layer.group_send)('12312312', { 'type': 'xxx.ooo', 'message': text_data }) def xxx_ooo(self, event): message = event["message"] self.send(message) def websocket_disconnect(self, message): """断开连接触发""" async_to_sync(self.channel_layer.group_discard)('12312312', self.channel_name) raise StopConsumer()
<script> function send_message() { var data = document.getElementById("txt").value; ws.send(data); } var ws = new WebSocket("ws://127.0.0.1:8002/chat/"); ws.onopen = function (event) { // 建立链接成功触发 var tag = document.createElement('div'); tag.innerText = "连接成功"; document.getElementById("message").appendChild(tag); }; ws.onmessage = function (event) { // 服务端有消息传来触发 var tag = document.createElement('div'); tag.innerText = event.data; document.getElementById("message").appendChild(tag); }; ws.onclose = function (event) { // 服务端主动关闭连接触发 var tag = document.createElement('div'); tag.innerText = "关闭连接"; document.getElementById("message").appendChild(tag); ws.close() }; </script>