小知识点记录

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,内部元素个数。
  • 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>
posted @ 2019-11-30 22:39  怀心抱素  阅读(283)  评论(0编辑  收藏  举报