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期 estdjango emplates est.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- 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('
', 1)
header_list = header.split('
')
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
"
"Upgrade:websocket
"
"Connection: Upgrade
"
"Sec-WebSocket-Accept: %s
"
"WebSocket-Location: ws://%s%s
"
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>