socket标准库是非常底层的接口库,socket是一种通用的网络编程接口
协议族Address Family
* AF_INET: IPV4
* AF_INET6: IPV6
* AF_UNIX: Unix Domain Socket,unix系统主机的socket
socket类型
* SOCK_STREAM: 面向连接的流socket, TCP协议
* SOCK_DGRAM: 无连接的datagram数据报socket, UDP协议
服务器
接收receive和发送send数据
其他一些方法:
socket.makefile(mode='rw', buffering=None, *,encoding=None, errors=None,newline=None)
返回一个与该socket**相关联**的类文件对象,recv和send方法被read和write方法代替
socket.getpeername()
返回socket remote end地址,元组(rattr, port)
socket.getsockname()
返回socket 自己的地址,(lattr, port)
socket.setblocking(flag)
flag为0,将socket设置为非阻塞模式,recv()不阻塞,没有数据就抛异常
客户端
socket.SHUT_RD | SHUT_WR | SHUT_RDWR
,关闭接收数据,关闭发送数据,全部关闭.关闭发送数据(SHUT_WR)后,对端每次recv立刻返回一个空bytes,服务端
connect((raddr, port))
,添加远端地址, 表示只接受指定地址的消息客户端
connect((raddr, port))
,仅添加远端地址,只接受指定地址的消息SocketServer简化了网络服务器的编写
4个同步类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer
2个Mixin类:ForkingMixIn和ThreadingMixIN,用来支持异步
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
class ForkingUDPServer(ForkingMixIN, UDPServer): pass
class ThreadingUDPServer(ThreadingMixIN, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIN, TCPServer): pass
fork创建多进程, thread是创建多线程
编程接口
socketserver.BaseServer(server_address, RequestHandlerClass)
RequestHandlerClass类必须是BaseRequestHandler类的子类,每一个请求会实例化对象,处理请求,拥有以下属性和方法:
self.request 是和客户端连接的socket对象
self.server 是server自己
self.client_address 是客户端地址
setup() 连接初始化
handle() 处理连接
finish() 连接清理
创建服务器步骤:
handle_request()
和永远处理serve_forever()
server的方法和属性
address_family
socket_type
shutdown_request(self,request) shutdown并close一个独立的请求
close_request(self,request) close一个独立的请求
get_request(self) -> (request, client_addr) 获得一个独立的请求,会阻塞, 相对于accept
fileno(self) -> server.socket 的文件描述符, selector使用
是一个非常轻巧的, 跨语言的通信模块, 官网
同步和异步
函数和方法被调用时,调用者是否直接得到最终的结果
直接得到就是同步调用,不直接得到就是异步调用,
阻塞和非阻塞
函数和方法被调用时,是否立即返回,
立即返回就是非阻塞,不立即返回就是阻塞
同步异步和阻塞非阻塞不相干
读取(read)IO两个阶段:
1. 数据准备阶段,内核从输入设备读取数据
2. 内核空间数据复制到用户进程缓冲区阶段
IO模型
同步IO模型包括: 阻塞IO, 非阻塞IO
Python中的IO多路复用
select库,实现了select,poll系统调用,通用性好,操作系统都支持,但性能较差,部分实现了epoll
实现了kqueque,epoll,devpoll,poll,select,
selectors.DefaultSelector会选择,当前系统性能最优的实现
编程步骤:
selector对象方法和属性
selector.get_map() -> dict, {fd:selectorkey, …}, key是文件描述符
aiohttp则是基于asyncio 模块实现的HTTP框架
可以使用aiohttp实现异步爬虫
例子:
from aiohttp import web
import asyncio
from aiohttp import ClientSession
async def handle(request: web.Request):
print(request.match_info)
print(request.query_string)
return web.Response(text=request.match_info.get('id', '0000'), status=200)
app = web.Application()
app.router.add_get('/{id}', handle)
web.run_app(app, host='0.0.0.0', port=9977)
# client
async def get_html(url: str):
async with ClientSession() as session:
async with session.get(url) as res:
print(res.status)
text = await res.text()
with open('bai', 'w', encoding='utf8') as f:
f.write(text)
url = 'http://www.baidu.com'
loop = asyncio.get_event_loop()
loop.run_until_complete(get_html(url))
loop.close()
另一个例子
流程:
Browser—(http request)—>WSGI Server—(解包,封装eviron)—>WSGI App处理数据
Browser<—(http response body)—WSGI Server<—(http response body)—WSGI App
Browser<—(http status, response header)—WSGI App使用WSGI Server提供的方法(start_response)
WSGI服务器参考库wsgiref
from wsgiref.simple_server import make_server, demo_app
server = make_server(ip, port, demo_app)
try:
server.serve_forever() # 另server.handle_request()
except:
server.shutdown()
server.server_close()
def demo_app(environ,start_response):
from io import StringIO
stdout = StringIO()
print("Hello world!", file=stdout)
print(file=stdout)
h = sorted(environ.items())
for k,v in h:
print(k,'=',repr(v), file=stdout)
start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')])
return [stdout.getvalue().encode("utf-8")]
# demo_app 接收两个参数
# environ: dict,保存的是http请求头部信息,key: REQUEST_METHOD, PATH_INFO, QUERY_STRING....
# start_response: 向Browser发送response status和header的方法,接收两个参数
# start_response(status:str, response_header:二元组, exc_info=None)
# return必须是iterable, return要在start_response调用之后
linux测试命令: curl -I url 获得请求头
curl -X POST -d data -X指定方法,-d传输数据
urllib库解析QUERY_STRING
urllib.parse.parse_qs(environ.get(‘QUERY_STRING’))
webob environ的解析库
request = webob.Request(environ) #将environ解析成request对象
request属性headers/method/path/query_string/GET/POST/params
GET返回url中的数据, POST返回body中提交的数据, params返回所有数据
webob.multidict.MultiDict
多值字典,add(key, value), key不能为int, 相同的key可以共存,
md.getone(k)有且只有一个, md.getall()返回所有
res = webob.Response() # 实例化response对象,参数可以定义响应的status, body等
return res(environ, start_response) # response实例可调用,返回一个可迭代对象
# 使用 webob.dec装饰器实现app,一个request一个reponse
from webob.dec import wsgify
@wsgify
def app(request: webob.Request) -> webob.Response:
res = webob.Response('body')
return res # return 可以是str,bytes,或者Response对象
__call__
方法作为调用函数,接收request模板技术
将数据填入html模板中作为response, 使用jinja2模块
# index.html
{% for id, name, age in userlist %}
- {{loop.index}} {{id}} {{name}} {{age}}
{% endfor %}
{{usercount}}}
# template.py
from jinja2 import Environment, PackageLoader, FileSystemLoader
env = Environment(loader=FileSystemLoader('/web/templates')) # 添执行时主模块的相对路径,或绝对路径
template = env.get_template('index.html') # 搜索env中loader文件夹下的index.html
res = template.render(d) # 渲染返回填好数据str, d为dict, key对应模板中的{{key}}
Django是采用MVC框架设计的开源WEB快速开发框架
自带ORM, Template, Form, Auth核心组件
Django 1.11版本同时支持python2和3
安装pip install django==1.11
/LIb/site-packages/django/bin/django-admin
django-admin startproject –help
创建项目:
django-admin startproject projectname .
个文件用途:
manage.py, 命令行工具, 应用创建, 数据库迁移等
blog/settings.py, 项目配置文件, 数据库参数等
blog/urls.py, URL路径映射配置
blog/wsgi, 定义WSGI接口信息, 一般无须改动
setting.py配置数据库连接
DATABASES={
'ENGINE': 'django.db.backends.mysql'
'NAME': 'blog',
'USER': 'peijun',
'PASSWORD': 'centos',
'HOST': '192.168.10.129',
'PORT': '3306'
}
安装数据库驱动, mysqlclient
pip install mysqlclient
windows下使用二进制包whl包
python manage.py startapp appname
在setting.py中INSTALLED_APPS添加刚创建的appname应用
文件作用:
admin.py 管理站点模型的声明文件,用于后台管理
models.py 模型层model类定义
views.py 定义URL响应函数
migrations包 数据迁移文件生成目录
apps.py 应用的信息定义文件
url(regex_pattern, view, kwargs=None, name=None)
url(r'^user/', include('user.urls'))
**include**动态导入指定app下的urls模块, 二级匹配使用
django.db.models column类型:
AutoField
BooleanField
NullBooleanField
CharField
TextField
IntegerField/BigIntegerField
DecimalField 使用python的Decimal实例表示十进制浮点数, max_digits/decimal_places
FloatField python的Float实例表示浮点数
DateField 使用python的datetime.date实例表示的日期, auto_now/auto_now_add
TimeField 使用python的datetime.time实例表示的时间
DateTimeField 使用python的datetime.datetime实例表示的时间
字段选项
db_column 表中字段的名称, 未指定, 使用属性名
primary_key 设置主键
unique 设置唯一键
default 设施缺省值
null 设置null
blank Django表单验证中, 是否可以不填写, 默认为False
db_index 设置索引
关系类型字段Relationships
ForeignKey 表示many to one多对一, ForeignKey(mode_class, on_delete=models.CASCADE), 推荐外键属性使用小写的类名作为标识符
ManyToManyField 表示多对多
intermediary model,可以添加中间模块, 参数添加through='Membership'
, 构成多对多Unlike normal many-to-many fields, you can’t use add(), create(), or set() to create relationships:
OneToOneField 表示一对一
不设置主键, 默认添加主键表名_id
一对多时, 一端自动创建_id后缀属性
定义端访问,使用对象.定义属性名(一般使用modles)
相关端访问,使用对象.小写模型类名_set
创建Model类
from django.db import models
class User(models.Model):
class Meta: # metadate 设置表名,联合主键等, 不同应用的表名不要相同
db_table = 'user'
column1 = models.CharField(max_length=128)
Meta “anything that’s not a field”
default_related_name related_query_name
db_table
ordering []
unique_together
indexs
模型操作:
模型类的objects属性, 是一个默认的manager, 用于和数据库交互, 也可以手动指定管理器
定义模型实例使用user = User()
user.save() INSERT
user.delet() DELETE
User.objects.create(name=’tom’) 创建记录并save()
模型类实例调用save(), delete()的时候,事务自动提交, save()和delete()都可以被覆盖, 增强功能
add() authors.add(author)
set() authors.set(authors)
remove()
clear() (https://docs.djangoproject.com/en/2.1/topics/db/queries/#additional-methods-to-handle-related-objects)
复制记录, 添加一条相同的记录, 仅主键不同
blog.pk = None; blog.save() # 多对多, 多对一是需要额外设置相关属性
querySet.update(content=’abc’) # 批量设置, 不会执行save(),
Making Queries
F 使用当前模块的字段值作为比较值(https://docs.djangoproject.com/en/2.1/topics/db/queries/#filters-can-reference-fields-on-the-model)
Field lookup
field__lookuptype=value
QuerySets Caching
limit 切片不会用的cache, 所有的元素都被迭代使用, 才会生成缓存
获取Related objects
QuerySet API reference 相关对象使用
查询集,QuerySet: 1.惰性求值, 只有查询集被使用是才查询数据库 2.拥有缓存,一个查询集可以被一个变量保存
返回查询集的方法叫做过滤器:
User.objects.all() :select * from User:
User.objects.all()[20:40] :limit offset查询集切片:
filter(column=’name’, pk=10) 筛选满足条件的记录
exclude()排除满足条件的记录 :where子句:
order_by(‘table_column’) :
Company.object.order_by(F(‘last_contacted’).desc(nulls_last=True))
null_last表示null排在最后
values() :记录用字典表示(column:value), 放入列表返回
pk
总是表示主键
返回单值的方法:
get() 只返回一条记录, 多或者少都会抛异常
count() 返回查询总条数
first() 返回第一条记录
last() 返回最后一条记录
exists() 查询是否有数据, 有则返回True
in_bulk([1, 2], field_name=’pk’)
some_queryset.filter(pk=entry.pk).exists():
字段查询表达式(Field Lookup)
语法: filter(foreignmodes__colname__method=value)
exact: filter(isdeleted__exact=False) 严格等于, 可省略不写
contains: exclude(title__contains=”b”) 等价于 not like ‘%b%’
startswith: filter(title__startwith=”w”)
endswith:
isnull/isnotnull: 是否为None
iexact/icontains/istartwith/iendswith: 忽略大小写
in: filter(pk__in=[1,2,3])
year,month, day, week_day, hour, minute, second: 对日期类型指定具体时间
Q对象
from django.db.models import Q
Q类接收条件, Q对象可以使用&(and), |(or), ~(not)操作
filter函数,如果混用关键字参数和Q对象, Q对象必须位于关键字参数前面,所有参数条件都会被and到一起
生成迁移文件
python manage.py makemigrations
执行迁移, 在数据库中生成表
python manage.py migrate
新建模板目录template, settings.py配置模板路径
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATES = [{'DIRS': [os.path.join(BASE_DIR, 'templates')],},]
模板使用分为两步:
.
点号访问容器内元素或对象内属性和方法, 调用方法不加括号, 变量未定义使用”” for标签
{% for athlete in athlete_list %}
{{athlete.name}}
{% endfor %}
for标签内变量:
forloop.counter 从1开始计数 forloop.revcounter 倒计数到1
{% for athlete in athlete_list reversed %} 反向迭代
{% ifequal val1 val2 %}{% ifnotequal val1 val2 %} 比较相等
{% csrf_token %} 跨站请求保护, 防止跨站攻击
CSRF(Cross-site request forgery) 跨站请求伪造, cookie授权, 伪造受信任用户
{# comment statement #} 单行注释
{% comment %} statement {% endcomment %} 多行注释
在变量被显示前修改它, 语法{{variable|handler}}|
两边没有空格
有的过滤器可以传参, :"para"
{{name|lower}}
{{name|first|upper}}
{{my_list|join:”,”}}
value|divisibleby:”2”|yesno:”True,False,None”, 能否被2整除, yesno可以只有两个参数true_false
value|add:”100”
|addslashes 在反斜杠和单引号或者双引号前面加上反斜杠
|length 容器的长度, str的长度
|default:”” 变量等价False则使用缺省值
|default_if_none:”” 变量为None则使用缺省值
|date:”n j Y” 格式化日期, n月j日Y年
第一个参数: request: HttpRequest
url中匹配到的分组也会依次按位置传入
获取request数据:
Response, JsonResponse,Django中有许多错误类, 实例可以作为view函数的返回值
from django.http import HttpResponseBadRequest, JsonResponse, HttpResponse
HttpResponse(status=401) # 添加状态码
cookie存储session id, 每一次请求都被附带,
session id有过期的机制, 过期后session和cookie分别在服务端和客户端被清除
session信息会消耗服务器内存, 同时在多服务器部署时, 要考虑session共享的问题redis,memcached
memcached: 数据库查询缓存, 提高网站访问速度
cookie_session的使用
参考初步理解JWT并实践使用
服务器生成一个标识(代表客户端ID), 并对这个标识使用算法签名(防止数据被客户端篡改), 组成JWT数据
下次客户端将JWT数据发回, 服务端就可以确认是否是认证过的用户
pip install pyjwt
jwt_token = jwt.encode({'payload': 'id'}, key, 'HS256') -> bytes
key是编码和解码用的密钥, 尽可能复杂, settings.py 中的SECRET_KEY是一个强密码,导入使用
from django.conf import settings
导入setting模块
设置超时
将"exp": int(datetime.datetime.now().timestamp()) + TOKEN_EXPIRE
加入payload, TOKEN_EXPIRE是超时时间
token以b'.'
分为三部分,header(jwt,algorithm), payload, signature
前两部分使用base64编码的, 用base64解码后可以得到源信息, 所有jwt不是用来加密的, 仅保证数据不被修改
alg = algorithms.get_default_algorithms()\['HS256']
newkey = alg.prepare_key(SECRET_KEY)
signing_input, \_, _ = token.rpartition(b'.')
sign = alg.sign(signing_input, newkey)
signature = base64.urlsafe_b64encode(sign)
生成token的过程:
前面两部分字典转换成json格式的字符串(注意冒号两边没有空格),再encode成bytes, 再用base64编码,
header和payload两部分由b'.'
相连, 再有这个部分和密钥key生成的签名(bytes), 再用base64编码,与前面相连
jwt解码
payload = jwt.decode(jwt_token, key, algorithms=\['HS256']) -> payload_data
RSA是公开的密钥密码体制, 有PK和SK, 非对称算法, 所有人都可以使用PK加密,只有拥有SK,才能对消息解密. 对极大整数做因数分解的难度决定了RSA算法的可靠性.
HMAC数字签名, 对称算法, 一个密钥和一个消息作为输入, 输入一个签名
消息双方都有密钥, 用于验证连接是否合法, (发送随机数, 双方算出结果, 验证)
或者用于确保消息不被篡改, (消息和签名绑定)
慢算法, 耗时长, 不同密码使用不同盐
bcrypt.gensalt() 生成盐
bcrypt.hashpw(password, salt) 生成加密密钥
password是bytes, 密钥由盐决定, 生成的加密密钥也是bytes, 盐就是前22个字节
bcrypt.checkpw(password, enci_password)
文档