html协议
浏览器请求
协议表示规定,意思就是必须按照这个来,不然你发出去的东西别人不认识
# 这里写的是请求方式,请求的内容 / 表示根目录 HTTP/1.1表示版本
GET / HTTP/1.1
# 请求的域名
Host: www.baidu.com
# 请求的连接方式,使用的是长连接模式
Connection: keep-alive
Upgrade-Insecure-Requests: 1
# 表示通过什么浏览器请求
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
# 表示可以接受的文件类型
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://www.baidu.com/
# 表示接受的压缩包格式
Accept-Encoding: gzip, deflate, br
# 表示接受的解析语言
Accept-Language: zh-CN,zh;q=0.9
超文本传输协议只是很多协议中的一种,还有https基于安全性的超文本传输协议
服务器的响应
服务器在收到浏览器的请求之后,根据请求内容和请求方式等等,调用对应的程序进行先关的响应,响应的内容也是必须符合规范,响应内容包裹响应头和响应体
响应体就是返回回来的响应页面,响应头包含了响应的一些规范信息,必须满足http协议的写法
响应头部分
# 申明响应的协议版本 响应后的状态码(200表示成功) ok这个可以随便写
HTTP/1.1 200 OK
Bdpagetype: 2
Bdqid: 0xb533b01200030d20
Cache-Control: private
# 响应的连接方式,表示长连接
Connection: Keep-Alive
# 响应的内容压缩格式
Content-Encoding: gzip
# 响应的内容解析格式和编码方式
Content-Type: text/html;charset=utf-8
# 两个时间
Date: Sat, 15 Dec 2018 05:03:20 GMT
Expires: Sat, 15 Dec 2018 05:03:20 GMT
# 响应的服务器
Server: BWS/1.1
Strict-Transport-Security: max-age=172800
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
这里还存在cookie的使用,这个后面再说
浏览器点击确定之后到页面显示完全
这中间到底经过了多少步骤
DNS服务器
我们在输入域名的时候实际上每个域名对应的是一个IP地址,计算机并不能进行解析郁闷,只能解析的是ip地址,所以平时我们输入的域名真实的是先在自己电脑上进行域名检索,如果本机上有则直接返回对应域名的ip,如果没有则进行连接外面进行访
先通过网关进行访问路由,路由进行连接最后到DNS网段进行搜寻找到对应的DNS服务器,服务器上进行解析提供的域名之后进行返回域名对应的ip.
拿到DNS解析的ip之后进行ip访问,还是通过网关和路由找到对应的ip,此时建立与服务器的三次握手,三次握手完成之后,客户端发送请求,服务端回复请求,之后挥手
需要注意的是向DNS服务器发送请求的协议实际上是UDP协议
当域名输入完成后本机与服务器发生的连接次数
总共存在10次包的传递,当然最初我也以为是11次,实际上需要之后10次.
首先三次握手的时候就是3次,四次挥手的时候就是4次,这里就有7次
在客户端与服务端进行通信的时候情况本身应该是:
客户端----->服务端
这一次是客户端请求,表示快把网页代码给我
服务端----->客户端
服务端收到之后给一个回复:好的我收到你的请求了
服务端----->客户端
服务端把客户端请求的数据发送给客户端
分割线--------------------------------------------------------------------------------------------------------------------------------------------------
以下就是四次挥手
服务端----->客户端
服务端已经把数据包发送给客户端了,现在再连接已经没有必要,所以服务端此时断开连接
客户端----->服务端
客户端收到断开连接请求,表态,我已经收到了,同意断开
客户端----->服务端
客户端处理完数据之后发送确认信息,我同意断开
服务端----->客户端
服务端收到客户端的确认信息之后进行返回信息,表示已经确认信息,等待2MSL时间后关闭
这里之所以在四次挥手之前客户端收到服务端的数据后没有回复确认信息,实际上利用抓包来看真的没有这个确认信息,根据tcp这种安全性的协议不应该嘛.事实上是因为设计协议的时候就考虑到,服务器发完之后会断开做四次挥手的动作,你客户端发送的确认信息已经没有意义,不管你确不确认服务端都会直接发送断开请求,一旦服务端发送断开请求后,服务端将无法主动回复,计算真的没有收到也不会再次发包,因此这里客户端回复一个确认包的意义就没有了,所以总共实际上是10次包的传递
cookie和session的区别
cookie使用的是浏览器进行暂时存储,cookie将信息存储在浏览器上,cookie通常使用在安全要求不高的使用场景,必去未登录状态下的购物车数据,比如浏览某些网页后,用户的点击信息被存在cookie中,当用户跳转到其他网站上去的时候,如果有这个网站的广告块,则cookie就会传入信息,系统就会智能的推荐商品
session的存储方式与cookie不同,session将数据存储在服务器,这就保证了数据的安全性,而且session是基于cookie的,session会返回一个加密后的省份识别码给浏览器中的cookie进行保存,别人计算破解后拿到的也只是一对加密后的代码,实际的信息还是拿不到,这就确保了其安全性.因此session使用的场景就是要求安全性比较高的场景.比如用户的登录
当如果禁用了cookie后,session还是能够正常使用,只是session生成的身份识别码不在cookie中,如果用户自己保存后并填写在请求头里面,session依然会正常运行,不会受到影响,只是这样非常不方便
实现一个静态服务器
虽然实际上不会让我们去写服务器,但是这里练习一下,了解整个过程,熟悉下各种头
from socket import *
from multiprocessing import Process
def main():
# 创建对象
server = socket(AF_INET, SOCK_STREAM)
# 配置断开释放端口功能
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 挂起服务器
server.bind(('', 8888))
# 配置为监听状态
server.listen()
while True:
# 客户端连接上就创建新对象
new_server, client_info = server.accept()
# 创建进程
p = Process(target=response_function, args=(new_server, client_info))
# 开启进程
p.start()
# 关闭外部的新对象
new_server.close()
def response_function(new_server, client_info):
data = new_server.recv(1024)
if not data: # 客户端断开连接的时候会发送一个空,此时直接关闭
new_server.close()
return
# print(f'来自{client_info}的请求:{data}')
# 服务器回复
# 先解析对应的请求信息,拆分成为列表,并且进行取出第一行,转码后取出请求的文件名
request_file_name = data.splitlines()[0].decode('utf-8').split(' ')[1]
# 如果请求的页面不存在则需要进行更改回复内容
try:
# 进行文件打开
with open(SERVER_ROOT+request_file_name, 'rb') as f:
# 读取出文件信息
response_body = f.read()
except:
# 如果报错则返回404
response_heads = b'HTTP/1.1 404 not find\r\n'
# 配置解释的类型
response_heads += b'Content-Type: text/html; charset=UTF-8\r\n\r\n'
# 响应内容
response_body = '没得'.encode('utf-8')
else:
# 准备好响应头数据
response_heads = b'HTTP/1.1 200 OK\r\n'
# 配置解释的类型
response_heads += b'Content-Type: text/html; charset=UTF-8\r\n\r\n'
print(request_file_name)
# 发送响应消息
new_server.send(response_heads+response_body)
new_server.close()
# 在外面定义服务器查询的根目录
SERVER_ROOT = './static'
if __name__ == '__main__':
main()
当然还是可以利用面向对象的思想将其封装称为一个可调用的包
但是需要注意的是在windows环境下,凡是使用了多进程的,而且原来的包里面写了
if __name__ == '__main__':
这种需要注意,坑爹的windows里面直接调用会进入死循环的,并且有时候是不会报错的,死循环的原因在于进程开启的那个地方,进程开启后子进程复制一份父进程的内存空间(这样说不准确,但是粗略理解就这样了),那么在这个py文件被调用的时候执行到这里时就会再次加载子进程,子进程里面跟父进程一样的还是有一个分裂进程,然后继续加载,这样就没完了,程序就会卡在进程分裂那里
解决方法很简单,谁导入,谁就要写
if __name__ == '__main__':
当然在linux里面就不会出现这种问题,随便调用