Python中的HTTP协议

文章目录

  • Python中的HTTP协议
    • 一、HTTP详解
    • 二、http协议的格式
      • 1、 HTTP请求
      • 2.HTTP响应
      • 3.HTTP格式
    • 三、例
      • Web静态服务器-1-显示固定的页面
      • Web静态服务器-2-显示需要的页面
      • Web静态服务器-3-多进程
      • Web静态服务器-4-多线程
      • Web静态服务器-5-非堵塞模式
        • 单进程非堵塞 模型
        • web静态服务器-单进程非堵塞
      • Web静态服务器-6-gevent版

一、HTTP详解

HTTP详解

二、http协议的格式

1、 HTTP请求

  • 步骤1:浏览器首先向服务器发送HTTP请求,请求包括:
  • 方法:GET还是POST,GET仅请求资源,POST会附带用户数据;
  • 路径:/full/url/path;
  • 域名:由Host头指定:Host: www.sina.com
  • 以及其他相关的Header;
  • 如果是POST,那么请求还包括一个Body,包含用户数据

2.HTTP响应

  • 步骤2:服务器向浏览器返回HTTP响应,响应包括:
  • 响应代码:200表示成功,3xx表示重定向,4xx表示客户端发送的请求有错误,5xx表示服务器端处理时发生了错误;
  • 响应类型:由Content-Type指定;
  • 以及其他相关的Header;
  • \r\n
  • 通常服务器的HTTP响应会携带内容,也就是有一个Body,包含响应的内容,网页的HTML源码就在Body中。
  • 步骤3:如果浏览器还需要继续向服务器请求其他资源,比如图片,就再次发出HTTP请求,重复步骤1、2。

3.HTTP格式

每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的

HTTP协议是一种文本协议,所以,它的格式也非常简单。

  • 3.1 HTTP GET请求的格式:
 GET /path HTTP/1.1Header1: Value1Header2: Value2Header3: Value3

每个Header一行一个,换行符是\r\n。

  • 3.2 HTTP POST请求的格式:
 POST /path HTTP/1.1Header1: Value1Header2: Value2Header3: Value3body data goes here...

当遇到连续两个\r\n时,Header部分结束,后面的数据全部是Body。

  • 3.3 HTTP响应的格式:
 200 OKHeader1: Value1Header2: Value2Header3: Value3body data goes here...

HTTP响应如果包含body,也是通过\r\n\r\n来分隔的

三、例

Web静态服务器-1-显示固定的页面

#coding=utf-8
import socketdef handle_client(client_socket):"为一个客户端进行服务"recv_data = client_socket.recv(1024).decode("utf-8")request_header_lines = recv_data.splitlines()for line in request_header_lines:print(line)# 组织相应 头信息(header)response_headers = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源response_headers += "\r\n"  # 用一个空的行与body进行隔开# 组织 内容(body)response_body = "hello world"response = response_headers + response_bodyclient_socket.send(response.encode("utf-8"))client_socket.close()def main():"作为程序的主控制入口"server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server_socket.bind(("", 7788))server_socket.listen(128)while True:client_socket, client_addr = server_socket.accept()handle_client(client_socket)if __name__ == "__main__":main()

Web静态服务器-2-显示需要的页面

#coding=utf-8
import socket
import redef handle_client(client_socket):"为一个客户端进行服务"recv_data = client_socket.recv(1024).decode('utf-8', errors="ignore")request_header_lines = recv_data.splitlines()for line in request_header_lines:print(line)http_request_line = request_header_lines[0]get_file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)print("file name is ===>%s" % get_file_name)  # for test# 如果没有指定访问哪个页面。例如index.html# GET / HTTP/1.1if get_file_name == "/":get_file_name = DOCUMENTS_ROOT + "/index.html"else:get_file_name = DOCUMENTS_ROOT + get_file_nameprint("file name is ===2>%s" % get_file_name) #for testtry:f = open(get_file_name, "rb")except IOError:# 404表示没有这个页面response_headers = "HTTP/1.1 404 not found\r\n"response_headers += "\r\n"response_body = "====sorry ,file not found===="else:response_headers = "HTTP/1.1 200 OK\r\n"response_headers += "\r\n"response_body = f.read()f.close()finally:# 因为头信息在组织的时候,是按照字符串组织的,不能与以二进制打开文件读取的数据合并,因此分开发送# 先发送response的头信息client_socket.send(response_headers.encode('utf-8'))# 再发送bodyclient_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)while True:client_socket, clien_cAddr = server_socket.accept()handle_client(client_socket)#这里配置服务器
DOCUMENTS_ROOT = "./html"if __name__ == "__main__":main()

Web静态服务器-3-多进程

#coding=utf-8
import socket
import re
import multiprocessingclass WSGIServer(object):def __init__(self, server_address):# 创建一个tcp套接字self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 允许立即使用上次绑定的portself.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 绑定self.listen_socket.bind(server_address)# 变为被动,并制定队列的长度self.listen_socket.listen(128)def serve_forever(self):"循环运行web服务器,等待客户端的链接并为客户端服务"while True:# 等待新客户端到来client_socket, client_address = self.listen_socket.accept()print(client_address)  # for testnew_process = multiprocessing.Process(target=self.handleRequest, args=(client_socket,))new_process.start()# 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的client_socket.close()def handleRequest(self, client_socket):"用一个新的进程,为一个客户端进行服务"recv_data = client_socket.recv(1024).decode('utf-8')print(recv_data)requestHeaderLines = recv_data.splitlines()for line in requestHeaderLines:print(line)request_line = requestHeaderLines[0]get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)print("file name is ===>%s" % get_file_name) # for testif get_file_name == "/":get_file_name = DOCUMENTS_ROOT + "/index.html"else:get_file_name = DOCUMENTS_ROOT + get_file_nameprint("file name is ===2>%s" % get_file_name) # for testtry:f = open(get_file_name, "rb")except IOError:response_header = "HTTP/1.1 404 not found\r\n"response_header += "\r\n"response_body = "====sorry ,file not found===="else:response_header = "HTTP/1.1 200 OK\r\n"response_header += "\r\n"response_body = f.read()f.close()finally:client_socket.send(response_header.encode('utf-8'))client_socket.send(response_body)client_socket.close()# 设定服务器的端口
SERVER_ADDR = (HOST, PORT) = "", 8888
# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./html"def main():httpd = WSGIServer(SERVER_ADDR)print("web Server: Serving HTTP on port %d ...\n" % PORT)httpd.serve_forever()if __name__ == "__main__":main()

Web静态服务器-4-多线程

#coding=utf-8
import socket
import re
import threadingclass WSGIServer(object):def __init__(self, server_address):# 创建一个tcp套接字self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 允许立即使用上次绑定的portself.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 绑定self.listen_socket.bind(server_address)# 变为被动,并制定队列的长度self.listen_socket.listen(128)def serve_forever(self):"循环运行web服务器,等待客户端的链接并为客户端服务"while True:# 等待新客户端到来client_socket, client_address = self.listen_socket.accept()print(client_address)new_process = threading.Thread(target=self.handleRequest, args=(client_socket,))new_process.start()# 因为线程是共享同一个套接字,所以主线程不能关闭,否则子线程就不能再使用这个套接字了# client_socket.close() def handleRequest(self, client_socket):"用一个新的进程,为一个客户端进行服务"recv_data = client_socket.recv(1024).decode('utf-8')print(recv_data)requestHeaderLines = recv_data.splitlines()for line in requestHeaderLines:print(line)request_line = requestHeaderLines[0]get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)print("file name is ===>%s" % get_file_name) # for testif get_file_name == "/":get_file_name = DOCUMENTS_ROOT + "/index.html"else:get_file_name = DOCUMENTS_ROOT + get_file_nameprint("file name is ===2>%s" % get_file_name) # for testtry:f = open(get_file_name, "rb")except IOError:response_header = "HTTP/1.1 404 not found\r\n"response_header += "\r\n"response_body = "====sorry ,file not found===="else:response_header = "HTTP/1.1 200 OK\r\n"response_header += "\r\n"response_body = f.read()f.close()finally:client_socket.send(response_header.encode('utf-8'))client_socket.send(response_body)client_socket.close()# 设定服务器的端口
SERVER_ADDR = (HOST, PORT) = "", 8888
# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./html"def main():httpd = WSGIServer(SERVER_ADDR)print("web Server: Serving HTTP on port %d ...\n" % PORT)httpd.serve_forever()if __name__ == "__main__":main()

Web静态服务器-5-非堵塞模式

单进程非堵塞 模型

#coding=utf-8
from socket import *
import time# 用来存储所有的新链接的socket
g_socket_list = list()def main():server_socket = socket(AF_INET, SOCK_STREAM)server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR  , 1)server_socket.bind(('', 7890))server_socket.listen(128)# 将套接字设置为非堵塞# 设置为非堵塞后,如果accept时,恰巧没有客户端connect,那么accept会# 产生一个异常,所以需要try来进行处理server_socket.setblocking(False)while True:# 用来测试time.sleep(0.5)try:newClientInfo = server_socket.accept()except Exception as result:passelse:print("一个新的客户端到来:%s" % str(newClientInfo))newClientInfo[0].setblocking(False)  # 设置为非堵塞g_socket_list.append(newClientInfo)for client_socket, client_addr in g_socket_list:try:recvData = client_socket.recv(1024)if recvData:print('recv[%s]:%s' % (str(client_addr), recvData))else:print('[%s]客户端已经关闭' % str(client_addr))client_socket.close()g_socket_list.remove((client_socket,client_addr))except Exception as result:passprint(g_socket_list)  # for testif __name__ == '__main__':main()

web静态服务器-单进程非堵塞

import time
import socket
import sys
import reclass WSGIServer(object):"""定义一个WSGI服务器的类"""def __init__(self, port, documents_root):# 1. 创建套接字self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 2. 绑定本地信息self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.server_socket.bind(("", port))# 3. 变为监听套接字self.server_socket.listen(128)self.server_socket.setblocking(False)self.client_socket_list = list()self.documents_root = documents_rootdef run_forever(self):"""运行服务器"""# 等待对方链接while True:# time.sleep(0.5)  # for testtry:new_socket, new_addr = self.server_socket.accept()except Exception as ret:print("-----1----", ret)  # for testelse:new_socket.setblocking(False)self.client_socket_list.append(new_socket)for client_socket in self.client_socket_list:try:request = client_socket.recv(1024).decode('utf-8')except Exception as ret:print("------2----", ret)  # for testelse:if request:self.deal_with_request(request, client_socket)else:client_socket.close()self.client_socket_list.remove(client_socket)print(self.client_socket_list)def deal_with_request(self, request, client_socket):"""为这个浏览器服务器"""if not request:returnrequest_lines = request.splitlines()for i, line in enumerate(request_lines):print(i, line)# 提取请求的文件(index.html)# GET /a/b/c/d/e/index.html HTTP/1.1ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])if ret:print("正则提取数据:", ret.group(1))print("正则提取数据:", ret.group(2))file_name = ret.group(2)if file_name == "/":file_name = "/index.html"# 读取文件数据try:f = open(self.documents_root+file_name, "rb")except:response_body = "file not found, 请输入正确的url"response_header = "HTTP/1.1 404 not found\r\n"response_header += "Content-Type: text/html; charset=utf-8\r\n"response_header += "Content-Length: %d\r\n" % (len(response_body))response_header += "\r\n"# 将header返回给浏览器client_socket.send(response_header.encode('utf-8'))# 将body返回给浏览器client_socket.send(response_body.encode("utf-8"))else:content = f.read()f.close()response_body = contentresponse_header = "HTTP/1.1 200 OK\r\n"response_header += "Content-Length: %d\r\n" % (len(response_body))response_header += "\r\n"# 将header返回给浏览器client_socket.send( response_header.encode('utf-8') + response_body)# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./html"def main():"""控制web服务器整体"""# python3 xxxx.py 7890if len(sys.argv) == 2:port = sys.argv[1]if port.isdigit():port = int(port)else:print("运行方式如: python3 xxx.py 7890")returnprint("http服务器使用的port:%s" % port)http_server = WSGIServer(port, DOCUMENTS_ROOT)http_server.run_forever()if __name__ == "__main__":main()

Web静态服务器-6-gevent版

from gevent import monkey
import gevent
import socket
import sys
import remonkey.patch_all()class WSGIServer(object):"""定义一个WSGI服务器的类"""def __init__(self, port, documents_root):# 1. 创建套接字self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 2. 绑定本地信息self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.server_socket.bind(("", port))# 3. 变为监听套接字self.server_socket.listen(128)self.documents_root = documents_rootdef run_forever(self):"""运行服务器"""# 等待对方链接while True:new_socket, new_addr = self.server_socket.accept()gevent.spawn(self.deal_with_request, new_socket)  # 创建一个协程准备运行它def deal_with_request(self, client_socket):"""为这个浏览器服务器"""while True:# 接收数据request = client_socket.recv(1024).decode('utf-8')# print(gevent.getcurrent())# print(request)# 当浏览器接收完数据后,会自动调用close进行关闭,因此当其关闭时,web也要关闭这个套接字if not request:new_socket.close()breakrequest_lines = request.splitlines()for i, line in enumerate(request_lines):print(i, line)# 提取请求的文件(index.html)# GET /a/b/c/d/e/index.html HTTP/1.1ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])if ret:print("正则提取数据:", ret.group(1))print("正则提取数据:", ret.group(2))file_name = ret.group(2)if file_name == "/":file_name = "/index.html"file_path_name = self.documents_root + file_nametry:f = open(file_path_name, "rb")except:# 如果不能打开这个文件,那么意味着没有这个资源,没有资源 那么也得需要告诉浏览器 一些数据才行# 404response_body = "没有你需要的文件......".encode("utf-8")response_headers = "HTTP/1.1 404 not found\r\n"response_headers += "Content-Type:text/html;charset=utf-8\r\n"response_headers += "Content-Length:%d\r\n" % len(response_body)response_headers += "\r\n"send_data = response_headers.encode("utf-8") + response_bodyclient_socket.send(send_data)else:content = f.read()f.close()# 响应的body信息response_body = content# 响应头信息response_headers = "HTTP/1.1 200 OK\r\n"response_headers += "Content-Type:text/html;charset=utf-8\r\n"response_headers += "Content-Length:%d\r\n" % len(response_body)response_headers += "\r\n"send_data = response_headers.encode("utf-8") + response_bodyclient_socket.send(send_data)# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./html"def main():"""控制web服务器整体"""# python3 xxxx.py 7890if len(sys.argv) == 2:port = sys.argv[1]if port.isdigit():port = int(port)else:print("运行方式如: python3 xxx.py 7890")returnprint("http服务器使用的port:%s" % port)http_server = WSGIServer(port, DOCUMENTS_ROOT")http_server.run_forever()if __name__ == "__main__":main()

Python中的HTTP协议相关推荐

  1. Python中的端口协议之基于UDP协议的通信传输

    阅读目录 UDP协议: 1.python中基于udp协议的客户端与服务端通信简单过程实现 2.udp协议的一些特点(与tcp协议的比较) 3.利用socketserver模块实现udp传输协议的并发通 ...

  2. python中基于tcp协议的通信(数据传输)

    tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据 ...

  3. Python中yield

    在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(Generator). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何类型 ...

  4. 由浅入深|让你彻底理解Python中的yield

    没有用过的东西,没有深刻理解的东西很难说自己会,而且被别人一问必然破绽百出.虽然之前有接触过python中的生成器的概念,但是只是走马观花,这两天的一次交谈中,别人问到了生成器,顿时语塞,死活想不起来 ...

  5. python中的for语句可以在任意序列_python在循环内任意增加迭代器

    python在循环内任意增加迭代器 我可能会以错误的方式处理此问题,但我想知道如何在python中处理此问题. 首先一些C代码: int i; for(i=0;i<100;i++){ if(i ...

  6. python中的in

    in有相当多的用处,平常用到最多in的地方可能就是for循环中了,比如: for i  in range(10): print(i) 此处的in就是i在0到10(不包含10)这个范围内了. 稍微探究一 ...

  7. 更深入理解 Python 中的迭代

    (点击上方公众号,可快速关注) 编译: linux中国 / MjSeven   英文:  Trey Hunner https://linux.cn/article-9681-1.html 深入探讨 P ...

  8. python中的迭代器,可迭代对象(详细剖析)

    文章目录 一 什么是迭代?什么是迭代器? 二 python中的迭代器 1. python中的迭代器协议 2. 迭代器的实现原理 三 python中的可迭代对象 1.可迭代对象 2.可迭代对象如何被迭代 ...

  9. python缓冲区_如何在Python中使用Google的协议缓冲区

    python缓冲区 When people who speak different languages get together and talk, they try to use a languag ...

最新文章

  1. 和12岁小同志搞创客开发:手撕代码,做一款人体感应灯
  2. Vertica的这些事lt;十二gt;—— vertica存储统计信息
  3. ElasticSearch Shard——本质上是做分布式扩展,副本对于集群的稳定性有很强的影响...
  4. Silverlight Training
  5. Stackoverflow 年度报告 2020:开发者最喜爱的数据库是什么?
  6. mysql openwrt 编译_如何编译OpenWrt
  7. PHP SQL查询结果在页面上是乱码
  8. 干掉Dubbo,换下Spring Cloud,这个架构有点厉害!
  9. 用于实时视频和图像去雾的优化对比度增强算法
  10. 奇兔recovery卡刷教程_如何使用recovery进行双清教程
  11. 华为云全流程护航《流浪方舟》破竹首发,打造口碑爆款
  12. 2021年模架行业如何乘风破浪?看行业知名人士畅谈模架市场趋势!
  13. linux zip和gzip的区别
  14. sinx/x的极限为什么是1_在sinx/x中当x→0时极限为什么为1?
  15. SQLserver2005 安装
  16. DataCastle-租金预测
  17. word只粘贴为文本快捷键设置
  18. 非正版win7升级win11教程
  19. 解决变频器干扰低压电子设备的经验
  20. 紧致卷积网络设计——Shift卷积算子

热门文章

  1. 200910阶段一C++虚析构
  2. 【SpringBoot零基础案例05】【IEDA 2021.1】若SpringBoot项目两种配置文件同时存在,哪种文件配置起作用?
  3. 【报错笔记】在做struts项目时,所有项目代码没问题但就是报404错误。
  4. 图解 Java 线程的生命周期,看完再也不怕面试官问了
  5. 网联能否一统天下,取决于三个问题
  6. 41.选项卡插件——tabs
  7. easyui datagrid local pager 表格本地分页
  8. Cocoa的MVC架构分析 cocoa的mvc实现
  9. VMware下主机与虚拟机通信问题
  10. 中国互联网哪来的所谓“所谓”的创新?“狗日”的腾讯究竟动了谁的蛋糕?...