一、HTTP协议介绍
1.使用谷歌/火狐浏览器分析
在Web应用中,服务器将网页发送给浏览器,浏览器实际上是将网页的HTML代码发送给浏览器供浏览器显示。而浏览器和服务器之间的传输协议是HTTP,所以:
HTML是一种用来定义网页的文本。如果你知道超文本标记语言,你可以写网页;
HTTP是一种通过网络传输HTML的协议,用于浏览器和服务器之间的通信。
Chrome浏览器提供了一套完整的调试工具,非常适合Web开发。
安装Chrome浏览器后,打开Chrome并从菜单中选择视图、开发人员和开发人员工具,以显示开发人员工具:
解释
元素显示网页的结构
网络显示浏览器和服务器之间的通信
让我们点击Network,确定第一个小红灯亮了,Chrome会记录所有浏览器和服务器之间的通信:
2.HTTP协议分析
当我们在地址栏输入www.sina.com时,浏览器会显示新浪的主页。浏览器在这个过程中做了什么?从网络的记录中,我们可以知道。在网络中,找到www.sina.com的记录,单击它,请求标题将显示在右侧。点击右侧的查看来源,可以看到浏览器发送给新浪服务器的请求:
2.1浏览器请求
解释
主要前两行分析如下,第一行:
GET / HTTP/1.1
GET表示读取请求,会从服务器获取网页数据,/表示URL的路径,URL始终以/开头,/表示第一页,最后一个HTTP/1.1表示采用的HTTP协议版本是1.1。目前HTTP协议的版本是1.1,但是大部分服务器也支持1.0版本。主要区别是1.1版允许多个HTTP请求重用一个TCP连接来加快传输速度。
从第二行开始,每一行都类似于Xxx: abcdefg:
主持人:www.sina.com
表示请求的域名是www.sina.com。如果一个服务器有多个网站,服务器需要通过Host来区分浏览器请求的是哪个网站。
2.2服务器响应
继续查找响应头,单击查看源,并显示服务器返回的原始响应数据:
HTTP响应分为报头和正文(正文可选)。我们在网络中看到的最重要的标题行如下:
HTTP/1.1 200 OK
200表示响应成功,以下OK为解释。
如果返回值不是200,那么通常还有其他函数,比如
失败的响应是404未找到:网页不存在
500内部服务器错误:内部服务器错误
...等等...
内容类型:文本/html
内容类型表示响应的内容,其中文本/超文本标记语言表示超文本标记语言网页。
请注意,浏览器依赖内容类型来判断响应内容是网页还是图片、视频或音乐。浏览器并不依靠网址来判断回复的内容,所以即使网址是http://www.baidu.com/meimei.jpg,,也不一定是图片。
HTTP响应的正文是HTML源代码。我们可以通过选择菜单栏中的“查看”、“开发人员”和“查看网页源代码”,直接在浏览器中查看HTML源代码:
浏览器解析过程
浏览器在读取新浪首页HTML源代码时,会解析HTML并显示页面。然后根据HTML中的各种链接,向新浪服务器发送HTTP请求,获取相应的图片、视频、Flash、JavaScript脚本、CSS等资源,最后显示完整的页面。所以我们可以看到网络下有很多额外的HTTP请求。
3.总结
3.1 HTTP请求
追踪新浪首页,我们来总结一下HTTP请求的流程:
3.1.1步骤1:浏览器首先向服务器发送HTTP请求,包括:
方法:GET或POST,GET只请求资源,POST会伴随用户数据;
路径:/full/URL/path;
域名:由主机标题指定:主机:www.sina.com
和其他相关标题;;
如果是开机自检,请求还包括一个包含用户数据的正文
3.1.1步骤2:服务器向浏览器返回一个HTTP响应,包括:
响应码:200表示成功,3xx表示重定向,4xx表示客户端发送的请求有错误,5xx表示服务器在处理过程中有错误;
响应类型:由内容类型指定;
和其他相关标题;;
通常服务器的HTTP响应都带有内容,也就是有一个Body包含响应的内容,网页的HTML源代码在Body中。
3.1.1步骤3:如果浏览器需要继续向服务器请求其他资源,比如图片,会再次发送HTTP请求,重复步骤1和2。
Web采用的HTTP协议采用了非常简单的请求-响应模式,大大简化了开发。我们写页面的时候,只需要在HTTP请求中发送HTML,不考虑如何附上图片和视频。如果浏览器需要请求图片和视频,会再发送一个HTTP请求。所以一个HTTP请求只处理一个资源(此时可以理解为TCP协议中的短连接,每个链路只得到一个资源。如果需要多个链接,则需要建立多个链接)
HTTP协议同时具有很强的扩展性。虽然浏览器请求的是http://www.sina.com的主页,但新浪可以用HTML等方式链接其他服务器的资源,将请求压力分散到各个服务器上。而且一个站点可以链接到其他站点,无数个站点相互链接,从而形成了万维网。
3.2 HTTP格式
每个HTTP请求和响应遵循相同的格式,一个HTTP包含头和体,其中体是可选的。
HTTP协议是文本协议,所以格式也很简单。
3 . 2 . 1 http get请求格式:
GET /path HTTP/1.1
标题1:值1
标题2:值2
标题3:值3
每个Header都有一行,换行符是\ r \ n
3 . 2 . 2 http post请求的格式:
POST /path HTTP/1.1
标题1:值1
标题2:值2
标题3:值3
身体数据在这里...
当遇到两个连续的\ r \ ns时,标题部分结束,后面的数据全部是正文。
3 . 2 . 3 http响应的格式:
200 OK
标题1:值1
标题2:值2
标题3:值3
身体数据在这里...
如果HTTP响应包含正文,它也用\ r \ n \ r \ n分隔。
请再次注意,正文的数据类型由内容类型标题决定。如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。
当有内容编码时,正文数据被压缩,最常见的压缩方法是gzip。所以,当你看到Content-Encoding: gzip的时候,你需要对Body数据进行解压缩,才能得到真实的数据。压缩的目的是减小Body的大小,加快网络传输速度。
二.网络静态服务器-1-显示固定页面
#coding=utf-8
导入套接字
def句柄_客户端(client_socket):
“为客户服务”
recv _ data = client _ socket . recv(1024)。解码(“utf-8”)
request _ header _ lines = recv _ data . split lines()
对于请求标题行中的行:
打印(行)
#组织相应的标题信息(标题)
response _ headers = " http/1.1 200 ok \ r \ n " # 200表示找到了此资源
response_headers += "\r\n" #与正文之间用一个空行隔开
#组织内容(正文)
response_body = "hello world "
response = response _ headers+response _ body
client _ socket . send(response . encode(" utf-8 "))
client_socket.close()
def main():
“作为程序的主要控制项”
server _ socket = socket . socket(socket。AF_INET,socket。SOCK_STREAM)
#设置在服务器先关闭的时候,也就是服务器波动4次之后可以立即释放资源,保证下次运行程序的时候可以立即绑定7788端口
server _ socket . setsockopt(socket。SOL_SOCKET,SOCKET。SO_REUSEADDR,1)
server_socket.bind((" ",7788))
server_socket.listen(128)
而真实:
client_socket,client _ addr = server _ socket . accept()
handle_client(client_socket)
if __name__ == "__main__ ":
main()
三.网络静态服务器-2-显示所需页面
#coding=utf-8
导入套接字
进口re
def句柄_客户端(client_socket):
“为客户服务”
recv _ data = client _ socket . recv(1024)。解码(' utf-8 ',错误=“忽略”)
request _ header _ lines = recv _ data . split lines()
对于请求标题行中的行:
打印(行)
http _ request _ line = request _ header _ lines[0]
get _ file _ name = re.match("[^/]+(/[^]*)”,http_request_line)。组(1)
打印(文件名= = = > % s“% get _ file _ name”)#进行测试
#如果您没有指定要访问哪个页面。例如,index.html
# GET / HTTP/1.1
如果get_file_name == "/:
get _ FIle _ name = DOCUMENTS _ ROOT+"/index . html "
else:
get _ FIle _ name = DOCUMENTS _ ROOT+get _ FIle _ name
打印(文件名= = = 2 > % s“% get _ file _ name”)#进行测试
尝试:
f =打开(get_file_name,“rb”)
除了IOError:
# 404表示没有这样的页面
response_headers = "未找到HTTP/1.1 404 \ r \ n "
response_headers += "\r\n "
response _ body ====抱歉,找不到文件= = = = "
else:
response _ headers = " HTTP/1.1 200 OK \ r \ n "
response_headers += "\r\n "
response_body = f.read()
f.close()
最后:
#由于表头信息在组织时是按字符串组织的,无法与二进制打开文件读取的数据合并,所以单独发送。
#首先发送响应的报头信息
client _ socket . send(response _ headers . encode(' utf-8 '))
#再次发送正文
client _ socket . send(response _ body)
client_socket.close()
def main():
“作为程序的主要控制项”
server _ socket = socket . socket(socket。AF_INET,socket。SOCK_STREAM)
server _ socket . setsockopt(socket。SOL_SOCKET,SOCKET。SO_REUSEADDR,1)
server_socket.bind((" ",7788))
server_socket.listen(128)
虽然真实:
client_socket,clien _ cAddr = server _ socket . accept()
handle_client(client_socket)
#在此配置服务器
DOCUMENTS_ROOT = "。/html "
if __name__ == "__main__ ":
main()
四.网络静态服务器-3-多进程
#coding=utf-8
导入套接字
进口re
导入多重处理
WSGIServer类(对象):
def __init__(self,server_address):
#创建tcp套接字
self . listen _ socket = socket . socket(socket。AF_INET,socket。SOCK_STREAM)
#允许立即使用最后一个绑定端口
self . listen _ socket . setsockopt(socket。SOL_SOCKET,SOCKET。SO_REUSEADDR,1)
#绑定
self . listen _ socket . bind(server _ address)
#变得被动并设置队列长度
self.listen_socket.listen(128)
def serve _ every(self):
"循环运行网络服务器,等待客户链接,为客户服务. "
而真实:
#等待新客户的到来
client_socket,client _ address = self . listen _ socket . accept()
打印(客户端地址)#用于测试
new_process =多重处理。进程(目标=self.handleRequest,args=(client_socket,)
new_process.start()
#因为子进程已经复制了父进程的套接字和其他资源,所以调用close的父进程不会关闭它们相应的链接
client_socket.close()
def handleRequest(自身,客户端套接字):
"使用新的流程为客户服务."
recv _ data = client _ socket . recv(1024)。解码(' utf-8 ')
打印(recv_data)
requestHeaderLines = recv _ data . split lines()
对于requestHeaderLines中的行:
打印(行)
request _ line = RequestHeaderlines[0]
get _ file _ name = re.match("[^/]+(/[^]*)”,request_line)。组(1)
打印(文件名= = = > % s“% get _ file _ name”)#进行测试
如果get_file_name == "/:
get _ FIle _ name = DOCUMENTS _ ROOT+"/index . html "
else:
get _ FIle _ name = DOCUMENTS _ ROOT+get _ FIle _ name
打印(文件名= = = 2 > % s“% get _ file _ name”)#进行测试
尝试:
f =打开(get_file_name,“rb”)
除了IOError:
response_header = "未找到HTTP/1.1 404 \ r \ n "
response_header += "\r\n "
response _ body ====抱歉,找不到文件= = = = "
else:
response _ header = " HTTP/1.1 200 OK \ r \ n "
response_header += "\r\n "
response_body = f.read()
f.close()
最后:
client _ socket . send(response _ header . encode(' utf-8 '))
client _ socket . send(response _ body)
client_socket.close()
#设置服务器的端口
服务器_ADDR =(主机,端口)= ",8888
#设置服务器服务的静态资源时的路径
DOCUMENTS_ROOT = "。/html "
def main():
httpd = WSGIServer(服务器_ADDR)
打印(“网络服务器:在端口%d上提供HTTP...\ n“% PORT”)
httpd . serve _ every()
if __name__ == "__main__ ":
main()
动词 (verb的缩写)Web静态服务器-4-多线程
#coding=utf-8
导入套接字
进口re
导入线程
WSGIServer类(对象):
def __init__(self,server_address):
#创建tcp套接字
self . listen _ socket = socket . socket(socket。AF_INET,socket。SOCK_STREAM)
#允许立即使用最后一个绑定端口
self . listen _ socket . setsockopt(socket。SOL_SOCKET,SOCKET。SO_REUSEADDR,1)
#绑定
self . listen _ socket . bind(server _ address)
#变得被动并设置队列长度
self.listen_socket.listen(128)
def serve _ every(self):
"循环运行网络服务器,等待客户链接,为客户服务. "
虽然真实:
#等待新客户的到来
client_socket,client _ address = self . listen _ socket . accept()
打印(客户端地址)
new_process = threading。thread(target = self . handlerequest,args=(client_socket,)
new_process.start()
#因为线程共享同一个套接字,所以主线程不能关闭,否则子线程不能再使用这个套接字。
# client_socket.close()
def handleRequest(自身,客户端套接字):
"使用新的流程为客户服务."
recv _ data = client _ socket . recv(1024)。解码(' utf-8 ')
打印(recv_data)
requestHeaderLines = recv _ data . split lines()
对于requestHeaderLines中的行:
打印(行)
request _ line = RequestHeaderlines[0]
get _ file _ name = re.match("[^/]+(/[^]*)”,request_line)。组(1)
打印(文件名= = = > % s“% get _ file _ name”)#进行测试
如果get_file_name == "/:
get _ FIle _ name = DOCUMENTS _ ROOT+"/index . html "
else:
get _ FIle _ name = DOCUMENTS _ ROOT+get _ FIle _ name
打印(文件名= = = 2 > % s“% get _ file _ name”)#进行测试
尝试:
f =打开(get_file_name,“rb”)
除了IOError:
response_header = "未找到HTTP/1.1 404 \ r \ n "
response_header += "\r\n "
response _ body ====抱歉,找不到文件= = = = "
else:
response _ header = " HTTP/1.1 200 OK \ r \ n "
response_header += "\r\n "
response_body = f.read()
f.close()
最后:
client _ socket . send(response _ header . encode(' utf-8 '))
client _ socket . send(response _ body)
client_socket.close()
#设置服务器的端口
服务器_ADDR =(主机,端口)= ",8888
#设置服务器服务的静态资源时的路径
DOCUMENTS_ROOT = "。/html "
def main():
httpd = WSGIServer(服务器_ADDR)
打印(“网络服务器:在端口%d上提供HTTP...\ n“% PORT”)
httpd . serve _ every()
if __name__ == "__main__ ":
main()
Copyright © 2017-2024 yunrg.cn All Rights Reserved. 山东云如故网络科技有限公司 版权所有 云如故 鲁ICP备19034752号