zoukankan      html  css  js  c++  java
  • 小知识点记录

    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期	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>
    
  • 相关阅读:
    转游戏开发做的第一款手机网游的经历和体会
    cocos2d-x 单点事件用法
    cocos2d-x编译成android 以及android开发环境一些经验分享
    apk 反编译用法 mac上apktool
    cocos2d-x 中LUA和平台之间的函数调用理解
    项目经验
    sprintf 的基本用法
    html5标签 ----------- canvas vue / html
    vue 基于element组件库实现表格下多个子级展示
    vue基于proxy实现服务器反向代理功能
  • 原文地址:https://www.cnblogs.com/zyyhxbs/p/11964335.html
Copyright © 2011-2022 走看看