Python -- 手动搭建HTTP服务器

目录

1.WSGI接口

2.新建WSGI服务器

3.使用if管理请求路径

4.使用字典管理请求路径

5.使用装饰器管理请求路径

6.requests模块的介绍


演示代码:

import re
 import socket
 from multiprocessing import Process
 
 
 class WSGIServer():
     def __init__(self, server, port, root):
         self.server = server
         self.port = port
         self.root = root
         self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         self.server_socket.bind((self.server, self.port))
         self.server_socket.listen(128)
     def handle_socket(self, socket):
         data = socket.recv(1024).decode('utf-8').splitlines()[0]
         file_name = re.match(r'[^/]+(/[^ ]*)', data)[1]
         # print(file_name)
 
         if file_name == '/':
             file_name = self.root + '/index.html'
         else:
             file_name = self.root + file_name
 
         try:
             file = open(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======='.encode('utf-8')
         else:
             response_header = 'HTTP/1.1 200 OK \r\n'
             response_header += '\r\n'
             response_body = file.read()
 
         finally:
             socket.send(response_header.encode('utf-8'))
             socket.send(response_body)
 
     def forever_run(self):
         while True:
             client_socket, client_addr = self.server_socket.accept()
             # self.handle_socket(client_socket)
             p = Process(target=self.handle_socket, args=(client_socket,))
             p.start()
             client_socket.close()
 if __name__ == '__main__':
     ip = '0.0.0.0'
     port = 8899
     server = WSGIServer(ip, port, './pages')
     print('server is running at {}:{}'.format(ip, port))
     server.forever_run()

1.WSGI接口

WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”:

 def application(environ, start_response):
     start_response('200 OK', [('Content-Type', 'text/html')])
     return '<h1>Hello, web!</h1>'

上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

  • environ:一个包含所有HTTP请求信息的dict对象;

  • start_response:一个发送HTTP响应的函数。

application()函数中,调用:

start_response('200 OK', [('Content-Type', 'text/html')])

就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个Header用一个包含两个strtuple表示。

通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。

然后,函数的返回值'<h1>Hello, web!</h1>'将作为HTTP响应的Body发送给浏览器。

有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。

整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。

不过,等等,这个application()函数怎么调用?如果我们自己调用,两个参数environstart_response我们没法提供,返回的str也没法发给浏览器。

所以application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。但是现在,我们只想尽快测试一下我们编写的application()函数真的可以把HTML输出到浏览器,所以,要赶紧找一个最简单的WSGI服务器,把我们的Web应用程序跑起来。

好消息是Python内置了一个WSGI服务器,这个模块叫wsgiref,它是用纯Python编写的WSGI服务器的参考实现。所谓“参考实现”是指该实现完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用。

2.新建WSGI服务器

  1. 创建hello.py文件,用来实现WSGI应用的处理函数。

     def application(environ, start_response):
         start_response('200 OK', [('Content-Type', 'text/html')])
         print(environ)
         return ['<h1>Hello, web!</h1>'.encode('utf-8'),'hello'.encode('utf-8')]

  2. 创建server.py文件,用来启动WSGI服务器,加载appliction 函数

     # 从wsgiref模块导入:
     from wsgiref.simple_server import make_server
     # 导入我们自己编写的application函数:
     from hello import application
     
     # 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
     httpd = make_server('', 8000, application)
     print("Serving HTTP on port 8000...")
     # 开始监听HTTP请求:
     httpd.serve_forever()

3.使用if管理请求路径

文件结构:

 ├── server.py
 ├── utils.py
 ├── pages
         └── index.html
 └── templates
     └── info.html

utlis.py文件

PAGE_ROOT = './pages'
 TEMPLATE_ROOT = './templates'
 
 def load_html(file_name, start_response, root=PAGE_ROOT):
     """
    加载HTML文件时调用的方法
    :param file_name: 需要加载的HTML文件
    :param start_response: 函数,用来设置响应头。如果找到文件,请求头设置为200,否则设置为410
    :param root: HTML文件所在的目录。默认PAGE_ROOT表示静态HTML文件,TEMPLATE_ROOT表示的是模板文件
    :return: 读取HTML文件成功的话,返回HTML文件内容;读取失败提示资源被删除
    """
     file_name = root + file_name
     try:
         file = open(file_name, 'rb')
     except IOError:
         start_response('410 GONE', [('Content-Type', "text/html;charset=utf-8")])
         return ['资源被删除了'.encode('utf-8')]
     else:
         start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
         content = file.read()
         return [content]
 
 def load_template(file_name, start_respone, **kwargs):
     """
    加载模板文件
    :param file_name: 需要加载的模板文件名
    :param start_respone: 函数,用来设置响应头。如果找到文件,请求头设置为200,否则设置为410
    :param kwargs: 用来设置模板里的变量
    :return: 读取HTML文件成功的话,返回HTML文件内容;读取失败提示资源被删除
    """
     content = load_html(file_name, start_respone, root=TEMPLATE_ROOT)
     html = content[0].decode('utf-8')
     if html.startswith('<!DOCTYPE html>'):
         return [html.format(**kwargs).encode('utf-8')]
     else:
         return content

service.py文件

from wsgiref.simple_server import make_server
 from utils import load_html, load_template
 
 def show_home(start_response):
     return load_html('/index.html', start_response)
 
 def show_test(start_response):
     start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
     return ['我是一段普通的文字'.encode('utf-8')]
 
 def show_info(start_response):
     return load_template('/info.html', start_response, name='张三',age=18})
 
 def application(environ, start_response):
     path = environ.get('PATH_INFO')
     # 处理首页请求(加载一个HTML文件)
     if path == '/' or path == '/index.html':
         result = show_home(start_response)
         return result
     # 处理test.html请求(返回一个普通的字符串)
     elif path == '/test.html':
         return show_test(start_response)
     # 处理info.html请求(加载一个模板并且返回)
     elif path == '/info.html':
         return show_info(start_response)
     # 其它请求暂时无法处理,返回404
     else:
         start_response('400 NOT FOUND', [('Content-Type', "text/html;charset=utf-8")])
         return ['页面未找到'.encode('utf-8')]
 
 httpd = make_server('', 8000, application)
 print("Serving HTTP on port 8000...")
 httpd.serve_forever()

4.使用字典管理请求路径

文件结构:

 ├── server.py
 ├── utils.py
 ├── urls.py
 ├── pages
         └── index.html
 └── templates
     └── info.html

urls.py文件:该文件里只有一个字典对象,用来保存请求路径和处理函数之间的对应关系。


 urls = {
     '/': 'show_home',
     '/index.html': 'show_home',
     '/test.html': 'show_test',
     '/info.html': 'show_info'
 }

server.py文件:

from wsgiref.simple_server import make_server
 from urls import urls
 from utils import load_html, load_template
 
 def show_home(start_response):
     return load_html('/index.html', start_response)
 
 def show_test(start_response):
     start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
     return ['我是一段普通的文字'.encode('utf-8')]
 
 def show_info(start_response):
     return load_template('/info.html', start_response, name='张三',age=18})
 
 def application(environ, start_response):
     path = environ.get('PATH_INFO')
     # 这里不再是一大堆的if...elif语句了,而是从urls字典里获取到对应的函数
     func = urls.get(path)
     if func:
         return eval(func)(start_response)
     # 其它请求暂时无法处理,返回404
     else:
         start_response('400 NOT FOUND', [('Content-Type', "text/html;charset=utf-8")])
         return ['页面未找到'.encode('utf-8')]
 
 httpd = make_server('', 8000, application)
 print("Serving HTTP on port 8000...")
 httpd.serve_forever()

5.使用装饰器管理请求路径

 from wsgiref.simple_server import make_server
 from utils import load_html, load_template
 
 g_url_route = {}
 
 
 def route(url):
     def handle_action(action):
         g_url_route[url] = action
 
         def do_action(start_response):
             return action(start_response)
 
         return do_action
 
     return handle_action
 
 
 @route('/index.html')
 @route('/')
 def show_home(start_response):
     return load_html('/index.html', start_response)
 
 
 @route('/test.html')
 def show_test(start_response):
     start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
     return ['我是一段普通的文字'.encode('utf-8')]
 
 
 @route('/info.html')
 def show_info(start_response):
     return load_template('/info.html', start_response, name='张三', age=18)
 
 
 def application(environ, start_response):
     file_name = environ.get('PATH_INFO')
     try:
         return g_url_route[file_name](start_response)
     except Exception:
         start_response('404 NOT FOUND', [('Content-Type', 'text/html;charset=utf-8')])
         return ['对不起,界面未找到'.encode('utf-8')]
 
 
 if __name__ == '__main__':
     httpd = make_server('', 8000, application)
     print("Serving HTTP on port 8000...")
     httpd.serve_forever()

6.requests模块的介绍

除了使用浏览器给服务器发送请求以外,我们还可以使用第三方模块requests用代码来给服务器发送器请求,并获取结果。

url = 'https://www.apiopen.top/satinApi'
 params = {'type': 1, 'page': 2}
 response = requests.get(url, params)
 print(response)
 
 # 方法二: 只能用于get请求
 url = 'https://www.apiopen.top/satinApi?type=1&page=1'
 response = requests.get(url)
 # print(response)
 
 # 2.获取请求结果
 print(response.headers)
 
 # 2)响应体(数据)
 # a.获取二进制对应的原数据(数据本身是图片、压缩文件、视频等文件数据)
 content = response.content
 print(type(content))
 
 # b.获取字符类型的数据
 text = response.text
 print(type(text))
 
 # c.获取json数据(json转换成python对应的数据)
 json = response.json()
 print(type(json))
 print(json)

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值