[TOC]
>[success] # 仿照网站请求
~~~
1.处理url,通过url 获取 http/https host port path 四个参数
2.例如 https://www.kancloud.cn/book, 首先获取http/https,在吧获取的连接
www.kancloud.cn/book,在进行拆分,获取域名和端口
~~~
>[info] ## 编写客户端
>[danger] ##### 处理url
~~~
1.socket 仿照浏览器处理,获取服务器请求
2.对url拆分,成协议,host,端口,和文件目录
3.利用find,来进行目录拆分,获取位置信息后,进行切片获取
4.将固定的东西放在字典中,省去if判断逻辑
~~~
~~~
def parsed_url(url):
"""
:param url: 获取网址的url
:return: 返回所需要的 http/https host port path 四个参数
"""
# 获取http 或者https
protocol = "http"
if url[:7] == "http://":
u = url.split("://")[1]
if url[:8] == "https://":
u = url.split("://")[1]
protocol = "https"
else:
u = url
# 把端口加域名 和 网站的文件目录分离
i = u.find("/")
if i == -1:
host = u
path = "/"
else:
host = u[:i]
path = u[i:]
# 处理host
port_dict = {
'http': 80,
'https': 443,
}
port = port_dict[protocol]
if ":" in host:
h = host.split(":")
host = h[0]
port = int(h[1])
return protocol, host, port, path
~~~
>[danger] ##### 尝试编写单元测试
~~~
def test_parsed_url():
http = 'http'
https = 'https'
host = 'g.cn'
path = '/'
test_items = [
('http://g.cn', (http, host, 80, path)),
('http://g.cn/', (http, host, 80, path)),
('http://g.cn:90', (http, host, 90, path)),
('http://g.cn:90/', (http, host, 90, path)),
#
('https://g.cn', (https, host, 443, path)),
('https://g.cn:233/', (https, host, 233, path)),
]
for t in test_items:
url, expected = t
u = parsed_url(url)
e = "parsed_url ERROR, ({}) ({}) ({})".format(url, u, expected)
assert u == expected, e
~~~
>[danger] ##### 编写client
~~~
1.拆分一个做协议判断的方法socket_by_protocol
2.一个循环获得服务器返回所有信息的方法response_by_socket
3.一个解析 header body 请求状态码的方法,巧妙利用元组可以被多个元素接受
4.在整体的大方法中,我们也可以用列表存储代替if判断
~~~
~~~
import socket,ssl
def socket_by_protocol(protocol):
"""
判断使用http 还是https 协议
"""
if protocol == "http":
s = socket.socket()
else:
s = ssl.wrap_socket(socket.socket())
return s
def response_by_socket(s):
"""
参数是一个 socket 实例
返回这个 socket 读取的所有数据
"""
response = b''
buffer_size = 1024
while True:
r = s.recv(buffer_size)
if len(r) == 0:
break
response += r
return response
def parsed_response(r):
"""
把 response 解析出 状态码 headers body 返回
状态码是 int
headers 是 dict
body 是 str
"""
header, body = r.split('\r\n\r\n', 1)
h = header.split('\r\n')
status_code = h[0].split()[1]
status_code = int(status_code)
headers = {}
for line in h[1:]:
k, v = line.split(': ')
headers[k] = v
return status_code, headers, body
def get(url):
protocol, host, port, path = parsed_url(url)
s = socket_by_protocol(protocol)
s.connect((host, port))
# 不用持续连接Connection: close
request = 'GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n'.format(path, host)
s.send(request.encode("utf-8"))
response = response_by_socket(s)
r = response.decode("utf-8")
parsed_response(r)
status_code, headers, body = parsed_response(r)
if status_code in [301, 302]:
url = headers['Location']
return get(url)
return status_code, headers, body
~~~
>[danger] ##### 服务端 -- server.py
~~~
1.利用 with 去做socket 连接,为了当端口用完进行关闭
2.接受到客户端访问服务器的请求头,对请求头的路径拆分出来
3.通过路由,将请求头 传递到路由映射函数
4.response_for_path 路由映射的方法,通过字典存储和路径对应的,处理函数,
其中k 保存的是函数地址,这样减少执行函数
5.在路由函数中利用字典get 的属性返回404 函数
6.将数据返回给客户端
~~~
~~~
def log(*args, **kwargs):
"""
用这个 log 替代 print
"""
print('log', *args, **kwargs)
def route_index():
"""
主页的处理函数, 返回主页的响应
"""
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = '<h1>Hello Gua</h1><img src="/doge.gif">'
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
def page(name):
with open(name, encoding='utf-8') as f:
return f.read()
def route_msg():
"""
msg 页面的处理函数
"""
header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'
body = page('html_basic.html')
r = header + '\r\n' + body
return r.encode(encoding='utf-8')
def route_image():
"""
图片的处理函数, 读取图片并生成响应返回
"""
with open('doge.gif', 'rb') as f:
header = b'HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\n'
img = header + b'\r\n' + f.read()
return img
def error(code=404):
"""
根据 code 返回不同的错误响应
目前只有 404
"""
# 之前上课我说过不要用数字来作为字典的 key
# 但是在 HTTP 协议中 code 都是数字似乎更方便所以打破了这个原则
e = {
404: b'HTTP/1.1 404 NOT FOUND\r\n\r\n<h1>NOT FOUND</h1>',
}
return e.get(code, b'')
def response_for_path(path):
"""
根据 path 调用相应的处理函数
没有处理的 path 会返回 404
"""
r = {
'/': route_index,
'/doge.gif': route_image,
'/msg': route_msg,
}
response = r.get(path, error)
return response()
def run(host='', port=3000):
"""
启动服务器
"""
# 初始化 socket 套路
# 使用 with 可以保证程序中断的时候正确关闭 socket 释放占用的端口
with socket.socket() as s:
s.bind((host, port))
# 无限循环来处理请求
while True:
# 监听 接受 读取请求数据 解码成字符串
s.listen(5)
connection, address = s.accept()
request = connection.recv(1024)
log('raw, ', request)
request = request.decode('utf-8')
log('ip and request, {}\n{}'.format(address, request))
try:
# 因为 chrome 会发送空请求导致 split 得到空 list
# 所以这里用 try 防止程序崩溃
path = request.split()[1]
# 用 response_for_path 函数来得到 path 对应的响应内容
response = response_for_path(path)
# 把响应发送给客户端
connection.sendall(response)
except Exception as e:
log('error', e)
# 处理完请求, 关闭连接
connection.close()
def main():
# 生成配置并且运行程序
config = dict(
host='',
port=3000,
)
# 如果不了解 **kwargs 的用法, 群里问或者看书/搜索 关键字参数
run(**config)
if __name__ == '__main__':
main()
~~~
- 网络原理
- 为搭建框架做准备
- 简单认识网路
- 自定义模拟网站案例
- 优化最终框架
- 数据存储 -- data
- 用户个人信息存储 -- User.txt
- 路由映射 -- routes
- 处理用户信息 -- routes_static.py
- 保存静态文件 -- static
- templates -- html 集中处理模块
- 首页 -- index.html
- 登陆 -- login.html
- 用户注册页面 -- register
- 日志模块 -- log.gua.txt
- 启动文件--server.py
- orm处理 -- model.py
- 日志模块 -- utils.py
- 两种数据库类型
- 传统数据库了解篇
- 前端快速入门
- JS简单使用入门
- css简单快速入门
- DJANGO
- virtualenv-创建虚拟环境
- 项目结构
- django-admin中文配置
- django-打印sql语句
- django-基础
- 认识MVC和MTV
- Django--初识
- Django--初识案例
- Django-FBV/CBV
- Django--常用input 交互
- Django-url
- Django-url.py 配置
- Django-include 使用
- Django-url name
- Django-ORM
- ORM-数据库配置
- ORM-model字段
- ORM-model字段解释
- ORM-字段选项
- ORM-查询
- ORM-四种常用查询方法
- ORM-三种获取数据
- ORM-其他查询方式
- ORM-条件查询双线
- ORM-Q和F条件使用
- ORM-三种数据库交互
- 案例 -- 一对多
- ORM-技巧/常见问题
- ORM-N+1 问题
- ORM-并发的处理
- ORM-数量查询、
- ORM-正向反向查询
- ORM-基础案例一
- ORM-基础一对多案例
- Django-templates
- Django-模板的继承
- Django-模板的过滤
- Django-自定义模板的过滤
- Django-cookie
- Django-cookies 装饰器
- Djang-session
- Django-CSRF
- Django-中间件 -- 后续了解
- Django- 缓存 -- 没有深入了解
- Django-form
- From-ajax
- form-内部验证处理
- form-属性
- form-常用的标签字段
- form-常用的下拉和选择
- form-widget速查
- Django-ajax序列化
- Django-多种ajax写法
- ajax-原生写法
- ajax-$写法
- ajax-ifram
- Django-ajax图片上传
- ajax-原始写法
- ajax-正常写法
- iframe+form
- 实战写法
- Django-常用自编写组件
- Django-双菜单组合搜索
- Django - 多菜单组合搜索
- Django-分页
- django-综合基础
- 综合基础-render
- django-admin
- admin-页面配置
- admin-字段配置
- admin-编辑页面
- admin-forms验证
- admin-创建抽象类
- django-验证码
- 验证码-第三方生成库
- 验证码-view.py使用
- 验证码-注意引入Monaco.ttf
- django-用户注册
- 注册-form 模块
- 注册-views 模块
- 注册-html模块
- 注册-model模块
- django-用户登录三种
- session登录
- form-session 写法
- view-写法
- Html-写法
- model-写法
- 继承类登录
- 外键关联登录
- django-简单的student 管理案例
- app-urls.py
- app-models.py配置
- admin-admin.py配置
- app-form.py 和数据库关联的写法
- app-FBV_views.py
- app-CBV_views.py
- templates-index.html
- django-博客系统
- APP目录-models.py 创建
- APP目录-基础展示数据分析
- APP目录-基础数据展示cls
- ListView
- DetailView
- FormView
- 额外功能拓建
- 添加文章搜索/用户文章查询功能
- 增加一个友情链接
- 增加一个评论模块
- App-利用Bootstrap4 搭建样式
- 项目crm
- 思维导图
- perfectCRM-项目名字
- settings.py-配置
- view.py-登陆/登出函数
- crm-app文件
- model.py-表的创建
- admin.py-注册后台
- view.py-视图层
- static-静态文件
- css
- bootstrap.min.css
- dashboard.css
- ie10-viewport-bug-workaround.css
- signin.css
- fonts
- imgs
- js
- jquery.js
- bootstrap.min.js
- holeder.js
- ie10-viewport-bug-workaround.js
- ie-emulation-modes-warning.js
- plugins
- html模板文件-templates
- crm
- index.html-首页模板