ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

引入web框架和优化过程

2021-03-15 20:32:07  阅读:105  来源: 互联网

标签:web 框架 url server html 引入 path data conn


简单的web框架搭建
软件开发目录规范
C/S架构

B/S架构

ps: B/S架构的本质就是一个C/S结构

搭建web框架
我们都知道如果我们想开发一个B/S的架构的软件,就必须要依赖于浏览器.

而为了让我们的页面能够被浏览器识别,我们就必须得遵循别人的协议标准.

当然你也可以不遵循,但是你得自己开发一个客户端,用于你自己的服务器上,要是你这个需求量也很大,别人都要下载你的客户端程序,而不是浏览器,当然它们就可以遵循你的标准协议使用了.

那么我们就可以想到有一个现成的客户端,并且功能还那么强大,我们就没必要重复造轮子了.

所有我们就基于浏览器去开发服务器就好了.

那么这个浏览器的协议是什么呢??

HTTP协议
我们就从http协议开始入手

http协议有四大特点

它是基于TCP/IP协议中的应用层,是在我们的应用程序中的协议

它是基于请求响应的协议

什么意思?

我们要知道我们的浏览器是一个“超级”客户端,在浏览器的网址栏中输入一个url就是一个请求,而服务端收到请求就会有一个响应去回复浏览器的请求信息.

都是客户端去和服务端发送请求的

无状态的

就是它不会记住你的服务器状态,你每一次来它都当作你是第一次来,并不会因为你来了几次就记住你

就好比说纵然见你千百遍,我却始终待你如初见.

这可得讲讲它的历史了,以前的浏览器啊,里面打开一个网页就是一写文章和报纸一样的,但是随着互联网慢慢的发展,报纸格式也就是说全文字的网页以及满足不了用户了,就变成了现在的支付宝啊,淘宝啊.这个在我们使用的过程中.我们是不是登录了下次就不用登录啦.你淘宝里面的购物车信息,下次登录还在.还有那个扫描二维码登录的.

这都是为了解决无连接的.我们会用到cookie,session.和taken技术了.这都可以起到保存我们的用户状态的作用.

短链接&无链接

就是每次浏览器和服务器连接之后,默认都是交互完毕之后就自动断开连接了.所有我们每次操作浏览器都会有很多的次的断开连接,再断再连的操作过程.

而还有一种就是长连接:

顾名思义,长连接就是一个默认不会断开俩者之间连接的.一般可以基于websocket实现.这个实现的还可以让服务器端给你浏览器发送请求. 就比如一写小网站些弹出广告啊,那些的.

那了解了http的四大特性,里面什么是请求什么是响应?

什么是响应
请求就是浏览器向服务器发送get/post请求,接收/上传 服务器的数据

响应就是服务器接收到客户端的请求信息,给予的回应,比如返回 一个 200 ok 的页面信息

我们再讲讲http的组成,为了方便管理和处理,我们的请求格式和响应格式组成的方式是差不多一样的.

请求格式
请求首行: =====> http的版本信息 和请求方式

请求头部 ====> 放的是一堆客户端的信息字典,一大堆的k,v键值对

/r/n/

请求体 (并不是所有的请求方式都有,get没有post有,post存放的是一些提交的敏感数据,比如说账号和密码)

请求格式图解
img

http 响应格式
响应首行 ====> 标识http协议的版本, 响应状态码

响应头 ===== > 一大堆k,v键值对

/r/n

响应体 =====> 返回给浏览器展示给用户看的数据.

响应格式图解:
img

介绍网络名词
请求方式:
1.get请求

朝服务器要数据

eg:输入url获取对应的数据

2.post请求

朝服务器提交数据

eg: 用户登录,向服务器提交用户名和密码数据 后端接收到数据就那些校验

3.get请求和post请求的区别

get请求发送的数据是在url中携带的,以?分割url和数据,并且数据与数据之间以&符号链接,所以是暴露在网页中的

eg:https://www.cnblogs.com/jkeykey/index?username=..

post请求发送的数据是在一个data的字典对象数据包里面的中,是另外存储的,并且是有加密手段的,更加的安全

eg: 1615792766169

get请求因为是url后面的,所以get请求携带的数据是有限的,而post携带数据是没有限制的.

get请求不如post请求安全,但一般的网络请求都是get.post一般用于敏感数据.

url
定义为: 统一资源定位符

格式:scheme:[//[user:password@]host:[:port][/]path[?query-string][#anchor]

参数介绍:

scheme:协议(例如:http/https/ftp.等)

user:password@ 用户的登录名和密码

host:服务器的ip地址或者域名

port: 服务器的端口号 (如果用的是协议,比如http(默认端口80),https(默认端口为443))

path: 访问资源的路径

query-string: 参数,一般为客户端发送给服务端的数据

anchor:锚(跳转到指定的锚点位置)

例如:url: https://www.cnblogs.com/jkeykey/p/14521413.html

对应关系

scheme: https

host:www.cnblogs.com

port: 443

path: /jkeyjkey/p/14521413.html

开始推理web框架
介绍了http的相关知识,现在我们终于可以搭建我们的web框架了

未遵循浏览器标准
未遵循浏览器标准的代码如下:

mport socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)


while True:
conn, client_addr = server.accept()
data_bytes = conn.recv(8192)
print(data_bytes) # 将浏览器发来的消息打印出来
"""
===================== HTTP协议的请求数据格式4部份 =====================
b'GET / HTTP/1.1\r\n # 请求首行(标识HTTP协议版本,当前请求方式)
Host: 127.0.0.1:8080\r\n # 请求头(一大堆k,v键值对)
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
\r\n' # \r\n
# 请求体(并不是所有的请求方式都有. get没有post有, post存放的是请求提交的敏感数据)

b''               
"""
conn.send(b'ok')
conn.close()

我们这样如果是用自己的客户端,发送回收打印是可以的.但是如果在浏览器中打开,是查看不到ok这个字段的.

那如何才能在浏览器上显示我们的内容,或者说html文件呢?

这就要看看浏览器的格式了.

我们以访问博客园服务器为例

我们访问: https://www.cnblogs.com/

右键检查,点击network,然后刷新一下页面,抓取到网页的数据包信息

响应相关信息可以在浏览器调试窗口的network标签页中看到。

img

点击view source之后显示如下图:

img

那我们知道了,响应格式

遵循http协议
那我们的代码就应该也要遵循他的协议

代码改成


import socket

server = socket.socket()

server.bind(("127.0.0.1", 8080))

server.listen(5)


while True:

conn,addr = server.accept()

data_bytes = conn.recv(1024)
print(data_bytes)

# 先发送我们的格式的首行信息和 相应状态 以及换行.
# 为什么是俩个 \r\n,那是因为第一个\r\n是状态码的换行,第二个才是与代码体的换行
conn.send(b"http/1.1 200 ok \r\n\r\n")
conn.send(b"hello world")


conn.close()
客户端访问的数据格式

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nPragma: no-cache\r\n
Cache-Control: no-cache\r\n
sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"\r\n
sec-ch-ua-mobile: ?0\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36\r\n
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n
Sec-Fetch-Site: none\r\n
Sec-Fetch-Mode: navigate\r\n
Sec-Fetch-User: ?1\r\n
Sec-Fetch-Dest: document\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
\r\n'
此时我们访问的页面,就可以看到我们的 hello word 信息了

1615795718487

web框架之显示html文件
然后现在我们就可以对我们的服务端进行优化了

现在有一个需求,让我们模拟,百度冲浪,输入一个url就可以获取到对应的页面内容

那这要怎么实现呢?

我们可以先拿到客户端数据中的url中的页面信息

代码演示

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

while True:
conn, client_addr = server.accept()
data_bytes = conn.recv(8192)
print(data_bytes)
'''
b'GET /index HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nC
br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
\r\n'
'''
path = data_bytes.split()[1]
print(path) # b'/index'
conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 因为要遵循HTTP协议,所以回复的消息也要加状态行
if path == b'/index':
response = b'you can you do not bb: %s' % path
elif path == b'/login':
response = b'you can you not bb: %s' % path
else:
response = b'404 Not Found'
conn.send(response)
conn.close()
访问index和login时

1615796501074

1615796529408

访问一个不存在的url时

1615796696317

优化web框架之urls_list
那么这时候,问题又来了. 一个url就要对应一个if分支,那么我如果有100个呢?

那么要写100j个if判断吗??这肯定是不行的,所以我们要通过字典或者列表来优雅的替代多分支

代码如下

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

将返回不同的内容部分封装成函数

def index(path):
s = "This is {} page table!".format(path)
return s


def login(path):
s = "This is {} page table!".format(path)
return s


def error(path):
return '404 not found: {}'.format(path)

定义一个url和实际要执行的函数的对应关系

urls = [
('/index', index),
('/login', login),
]

while True:
conn, client_addr = server.accept()
data_bytes = conn.recv(8192)
print(data_bytes)
path = data_bytes.split()[1].decode('utf-8') # 分离出的访问路径, 再把收到的字节类型的数据转换成字符串
print(path) # '/index'
conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 因为要遵循HTTP协议,所以回复的消息也要加状态行

func = None # 定义一个保存将要执行的函数名的变量
for url in urls:
if path == url[0]:
func = url[1]
break
if func:
response = func(path)
else:
response = error(path)

# 返回具体的响应消息
conn.send(response.encode('utf-8'))
print(response.encode('utf-8'))
conn.close()
后缀/index路径访问

1615798206755

后缀/login

1615798274982

路径不存在时

1615798294815

知识点
动静态网页

静态网页
静态网页就是我们之前用的前端的知识搭建的,内容是写死的,比如

111

用这样的标签搭建的网页就是静态网页.

那么动态网页就是和这个相反的,标签内的内容是活,即是后端获取的, 比如

{{ name }}

,用这样的标签搭建的网页就是动态网页.

最大的区别就是,静态的网页,不改文本的内容是不会变的,而动态页面可以通过改后端的数据来改变网页中的内容.

静态网页详细介绍: 返回具体的html文件

我们之前用渲染给客户端的数据都是str字符串类型的,那我们怎么讲html文件中的内容渲染到浏览器上呢?

一样的思想,可以先将html文件中的内容全部拿到,一起发送给浏览器,让其渲染网页即可

代码为

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

def get_f(path):
import os
path = r"./templates"+path+".html"
with open(path,'rb') as f:
html_data = f.read()
return html_data

将返回不同的内容部分封装成函数

def index(path):
s = get_f(path)
return s


def login(path):
s = get_f(path)
return s


def error(path):
return bytes('404 not found: {}'.format(path).encode("utf-8"))

定义一个url和实际要执行的函数的对应关系

urls = [
('/index', index),
('/login', login),
]

while True:
conn, client_addr = server.accept()
data_bytes = conn.recv(8192)
print(data_bytes)
path = data_bytes.split()[1].decode('utf-8') # 分离出的访问路径, 再把收到的字节类型的数据转换成字符串
print(path) # '/index'
conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 因为要遵循HTTP协议,所以回复的消息也要加状态行

func = None # 定义一个保存将要执行的函数名的变量
for url in urls:
if path == url[0]:
func = url[1]
break
if func:
response = func(path)
else:
response = error(path)

# 返回具体的响应消息
conn.send(response)
conn.close()
当为index时

1615800072631

当为login时

1615800142961

动态网页
动态网页: 后端获取当前的时间展示到index.html页面上

代码如下

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

将返回不同的内容部分封装成函数

def _open_read(filename):
with open(filename, 'rt', encoding='utf-8') as f:
return f.read()


def index(path):
return _open_read(r'.\templates\index.html')


from datetime import datetime
def login(path): # !!!动态网页!!: 后端获取当前时间展示到html页面上
data = _open_read(r'.\templates\login.html')
data = data.replace('login', "login" + datetime.now().strftime('%Y-%m-%y %X'))
return data


def error(path):
return _open_read(r'.\templates\error.html')

定义一个url和实际要执行的函数的对应关系

urls = [
('/index', index),
('/login', login),
]

while True:
conn, client_addr = server.accept()
data_bytes = conn.recv(8192)
print(data_bytes)
path = data_bytes.split()[1].decode('utf-8') # 分离出的访问路径, 再把收到的字节类型的数据转换成字符串
print(path) # '/index'
conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 因为要遵循HTTP协议,所以回复的消息也要加状态行

func = None # 定义一个保存将要执行的函数名的变量
for url in urls:
if path == url[0]:
func = url[1]
break
if func:
response = func(path)
else:
response = error(path)

# 返回具体的响应消息
conn.sendall(response.encode('utf-8'))
print(response.encode('utf-8'))
conn.close()
优化web框架之wsgiref模块
使用wsgiref模块代替之前 web框架的socket server 部分

我们之前写的简单的web框架有俩个缺陷

代码重复

服务端socket代码需要我们自己写.如果好多服务端就要写好多个socket模块的服务端

http格式的数据得自己处理

并且只能拿到url后缀 其他数据获取得通过re正则去header那个字符串里面取,太麻烦了.

而利用wsgiref模块优势

wsgiref模块帮助我们封装了socket代码

帮我们处理http格式的数据

wsgiref模块就是一个web服务端网关接口"

请求来的时候会帮助你自动拆分http格式数据并封装成非常方便处理的数据格式 --->env大字典

响应走的时候会帮你将数据再打包成符合http格式的数据.

ps: 所以wsgiref模块会为我们做服务器对客户端的请求和响应数据,就不需要我们指定编码和解码了.而且取数据很方便

我们之前的代码都是写在一个文件内,我们都知道这是不允许的,所以我们应该对这些方法和代码进行区分,分文件存.

我们总结一下,我们之前的功能除了服务器发送还有三个

1.urls_list 一个url后缀和函数(类也可以)对应关系

2.views url对应的函数(类)的函数书写

3.templates 模块.存放一些html文件

所以我们现在可以讲这三个功能查分开来

这样做的好处是解耦合,拓展方便,只要按照 urls.py---> view.py ---->templates文件夹下的html 就可以搭建我们的web了.

现在我们的服务端启动文件可以为

from wsgiref.simple_server import make_server
from views import *
from urls import *


def run_server(request, response):
"""
函数定义什么都无所谓
:param request: 请求相关的所有数据
是一个大字典,wsgires模块帮你处理好http格式的数据,封装成一个字典方便你的后续操作
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据,返回的格式必须为一个二进制格式
"""
response("200 OK", []) # 响应首行 响应头
print(request)
"""
字典的其中一部分
'PATH_INFO': '/login', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8080'
"""
path_info = request.get("PATH_INFO")
print(path_info)

func = None
for url in urls_list:
if path_info == url[0]:
func = url[1]
break
if func:
data = func(request)
else:
data = error(request)

return [data.encode("utf-8")]


if name == 'main':
server = make_server("127.0.0.1", 8080, run_server)
"""
会实时监听 127.0.0.1:8080 地址 只要有客户端来了
都会交给run函数处理(加括号触发run函数的运行)

flask启动源码
    make_server("127.0.0.1",8080,obj)
    __call__
"""
server.serve_forever()  # 服务器一直开启

urls.py

"""
url后缀对应函数的对应关系文件
"""

from views import *


urls_list = [
("/index", index),
("/login", login),

]
views.py

"""
函数脚本,处理url对应的函数
"""


def _open_read(filename):
with open(filename, 'rt', encoding='utf-8') as f:
return f.read()


def index(request):
return _open_read('templates/index.html')


def login(request): # !!!动态网页!!: 后端获取当前时间展示到html页面上
from datetime import datetime
data = _open_read('templates/login.html')
data = data.replace('login', datetime.now().strftime('%Y-%m-%y %X'))
return data


def error(request):
return _open_read('templates/error.html')
优化方案之jinja2模块
到现在我们使用的网页大部分都是静态网页,动态网页也是通过python的方法

那我们优化这种局部的渲染的.

那就要讲到另外一个模块了jinja2,

jinja2作用: jiaja2的原理就是字符串替换,我们只要在html页面中遵循jinja2的语法规范写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容效果.

要使用这个模块,我们得事先安装,安装了flask的不用安装,flask自动携带jinja2,默认使用的就是这个.

安装命令:通过清华的地址,下载会快一点

pip3 install -ihttps://pypi.tuna.tsinghua.edu.cn/simple jinja2

jinja2的模板语法
// 定义变量使用双花括号
{{ 变量名称 }} // 该变量可以是python内的所有数据类型

// 执行for循环,if流程控制 为花括号内左右%
{% for i in list %}
{{ i.id }} # 不仅支持python的数据类型的语法,还支持点类属性取值的方法
{% endfor %}
那我们将这个运用到我们的框架中

使用jinja2优化网页内容

例子: 将user表中的数据渲染到 user.html网页中

创建user表

create database db777 charset utf8;
use db777
create table user(
id int primary key auto_increment,
username varchar(16),
pwd varchar(14),
sex enum("男","女"),
hobbies set("吃饭","睡觉","打土匪","打劫")
);
insert into user(username,pwd,sex,hobbies) values
("jkey","123","男","吃饭,打土匪"),
("土匪","123","男","吃饭,打劫"),
("派大星","123","男","打土匪");
执行文件不需要更改

所以执行文件用上面的即可

urls.py

"""
url后缀对应函数的对应关系文件
"""

from views import *


urls_list = [
("/index", index),
("/login", login),
("/user", user)
]
views.py

"""
函数脚本,处理url对应的函数
"""


def _open_read(filename):
with open(filename, 'rt', encoding='utf-8') as f:
return f.read()


def index(request):
return _open_read('templates/index.html')


def login(request): # !!!动态网页!!: 后端获取当前时间展示到html页面上
from datetime import datetime
data = _open_read('templates/login.html')
data = data.replace('login', datetime.now().strftime('%Y-%m-%y %X'))
return data


def _open_mysql():
import pymysql
conn = pymysql.connect(
host="127.0.0.1",
port=3306,
user="root",
passwd="jzd123",
db="db777",
charset='utf8',
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
affected_rows = cursor.execute("select * from user;")
if affected_rows:
user_dict_list = cursor.fetchall()
return user_dict_list
else:
print("对不起你要查找的数据不存在")
cursor.close()
conn.close()


def user(request):
from jinja2 import Template
data = _open_read("templates/user.html")
user_dict_list = _open_mysql()
if user_dict_list:
temp = Template(data)
res = temp.render(user_dict_list=user_dict_list)
return res
return error(request)


def error(request):
return _open_read('templates/error.html')
templates/user.html

Title

用户表信息

{% for user_dict in user_dict_list %} {% endfor %}
序号 名称 密码 性别 爱好
{{user_dict.id}} {{user_dict.username}} {{user_dict.pwd}} {{user_dict.sex}} {{user_dict.hobbies}}
ps:注意,浏览器只会识别html和css以及JavaScript,这个模板解析是在python后端处理好的,变成一个html然后再发送给浏览器渲染的,不是浏览器帮我们解析的模块语法.

致此我们的web简单的框架搭建就已经推理完毕了,这个过程最重要的就是对知识点的学习,以及整个的推导过程,代码不是很重要.重要的是推理过程.

思想一定要想,学习起来才快

总结
1.自定义简易版本web框架请求流程图
img

服务端开启使用wsgiref模块搭建的启动文件,一般配置了就不需要改变.

所以我们需要修改的文件以及步骤为 urls.py,views.py,templates模板下的html文件.

其中需要注意的是views.py中获取数据库的数据,渲染到html文件中,可以传参进去,可以是任何的python数据类型,模板语法也需要注意.

一定要敲,才能发现自己的薄弱点.

流程图流程
浏览器客户端.

wsgiref模块

请求来: 处理浏览器请求,解析浏览器http格式的数据,封装成大字典(PATH_INFO中存放的就是url的访问资源的路径)

相应去: 将数据打包成符合http协议的格式,再返回给浏览器

后端

urls.py

---> 找用户输入的路径有没有与视图函数对应关系的,有就去views.py内找到对应的函数

views.py

功能1(静态): 视图函数找templates中的html文件,返回给wsgiref做http格式的封包处理,再返回给浏览器.

功能2(动态):视图函数通过pymysql链接数据库,通过jinja2模板语法将数据库中的数据在templates文件夹下的html文件做一个数据的动态渲染, 最后返回给wsgiref做HTTP格式的封包处理, 再返回给浏览器.

功能3(动态): 也可以通过jinja2模板语法对tmpelates文件夹下的html文件进行数据的动态渲染, 渲染完毕, 再经过wsgiref做HTTP格式的封包处理, 再返回给浏览器.

基本使用流程

wsgiref模块: socket服务端(后端)

from wsgiref.simple_server import make_server


def run_server(request, response):
"""
函数名定义什么都无所谓, 我们这里就用run_server.
:param request: 请求相关的所有数据.
是一个大字典, wsgiref模块帮你处理好http格式的数据 封装成了字典让你更加方便的操作
:param response: 响应相关的所有数据.
:return: 返回给浏览器的数据, 返回个格式必须是'return [二进制格式的数据]' 这种样式
"""
response('200 OK', []) # 响应首行 响应头
return [二进制格式的数据]

if name == 'main':
server = make_server(host, port, app) # app=run_server
server.serve_forever()

urls.py:路由与视图函数对应关系

urls = [(路由,视图函数)]

views.py: 视图函数

def 视图函数(request):
    pass

# pymysql模块: socket服务端(后端)与数据库交互
import pymysql


conn = pymysql.connection(host, port, user, password, database, charset='utf8')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
affected_rows = cursor.execute(sql);
cursor.fetchall()
cursor.close()
conn.close()

# jinja2模块:后端与http文件交互,本质是一种替换操作
    from jinja2 import Template
    temp = Template(data)  # data为要操作的替换的数据
    retuen temp.render(user=user_date)  #user 是传入给html页面的操作变量

templates 文件夹:管理html文件

html中使用jinja2模块的语法:
    定义变量: {{  user }}
    for循环/if流程 :  {% for i in user %} ... {% endfor %}
        如果i是一个对象,那么可以通过i.xx或者i[xx]来获取值.

标签:web,框架,url,server,html,引入,path,data,conn
来源: https://www.cnblogs.com/jkeykey/p/14539810.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有