服务器采购框架合同协议书范本,手写一个满足WSGI协议的Server

在做Web开发时,一个很重要的概念就是服务端和应用程序之间的沟通协议,比如java中的servlet,由于servlet的存在,使得用java开发的web程序既可以跑在tomcat上,也可以是jetty。反之亦然。而在python中,对应的协议也就是WSGI协议,本文的目标就是实现一个可以支持python主流框架的web服务器,也帮助自己加强对WSGI协议的理解。

实验环境:

python3.5

一个简单的服务器实现

这一节并不会直接给出一个遵循WSGI协议规范的服务器,只是单纯从如何与客户端通信的角度来考虑实现。我们都知道,HTTP协议是建立在TCP协议的基础上,所以首先我们借助python标准库中的socket来实现TCP通信。下面是我的实现代码和解释:

# wsgi_a.py

import socket

HOST, PORT = '', 8888

listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

listen_socket.bind((HOST, PORT))

listen_socket.listen(1)

print('Serving HTTP on port %s ...', PORT)

# run or not

flag = True

while flag:

try:

client_connection, client_address = listen_socket.accept()

request = client_connection.recv(1024)

print(request)

http_response = """\

HTTP/1.1 200 OK

Hello, World!

"""

client_connection.sendall(http_response.encode())

client_connection.close()

except KeyboardInterrupt:

flag = False

print('exit')

这里需要说明的是关于socket的标准库中的基本函数及常量:

socket.socket(socket.AF_INET, socket.SOCK_STREAM) 返回一个socket对象,其中第一个参数需指明IP地址类型(IPv4, IPv6, ...),第二个参数用来指明通信的协议,这里两个参数的意思分别为(IPv4, TCP)。

socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)设置套接字重用。

socket.bind((HOST,PORT))绑定端口

socket.listen(1) 设置客户端的连接个数

socket.accept() 阻塞监听

打开终端。命令行中运行该脚本并在浏览器中输入

http://127.0.0.1:8888

即可查看结果。

满足WSGI协议的服务器

简单版本的服务器仅仅只是实现了与客户端之间的通信,同时将请求处理也放在了服务器里,并没有将两者分开。也没有对现有的主流框架进行支持。因此为了实现一个通用的Web服务器,根据WSGI协议,我们需要添加两个关键的部分。一个传给应用端的上下文环境,并一个是需要给应用端调用的start_response函数。详情可以参照我之前的翻译PEP333。

#wsgi_b.py

import socket

import sys, io

from datetime import date

class WSGIServer(object):

"""docstring for WSGIServer"""

def __init__(self, host, port, application):

self.host = host

self.port = port

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((self.host, self.port))

self.listen_socket.listen(1)

self.flag = True

self.application = application

print('Serving HTTP on port %s ...', self.port)

def get_environ(self, request_data):

env = {}

# CGI variables

path, server, *args = request_data

env['REQUEST_METHOD'], env['PATH_INFO'], _= path.split()

env['SERVER_NAME'], env['SERVER_PORT'] = self.host, str(self.port)

# WSGI variables

env['wsgi.version'] = (1, 0)

env['wsgi.url_scheme'] = 'http'

env['wsgi.input'] = io.StringIO(self.request_data.decode())

env['wsgi.errors'] = sys.stderr

env['wsgi.multithread'] = False

env['wsgi.multiprocess'] = False

env['wsgi.run_once'] = False

return env

def make_server(self):

while self.flag:

try:

self.client_connection, self.client_address = self.listen_socket.accept()

self.request_data = self.client_connection.recv(1024)

request_data = self.request_data.decode().splitlines()

env = self.get_environ(request_data)

result = self.application(env, self.start_response)

self.make_response(result)

except KeyboardInterrupt:

self.flag = False

print('exit')

def make_response(self, result):

try:

status, response_headers = self.headers_set

response = 'HTTP/1.0 {status}\r\n'.format(status=status)

for header in response_headers:

response += '{0}: {1}\r\n'.format(*header)

response += '\r\n'

for data in result:

response += data.decode()

self.client_connection.sendall(response.encode())

finally:

self.client_connection.close()

def start_response(self, status, response_headers, exc_info=None):

# Add necessary server headers

server_headers = [

('Date', date.today().strftime('%Y-%m-%d')),

('Server', 'WSGIServer 0.2'),

]

self.headers_set = [status, response_headers + server_headers]

这个版本的服务器实现了一个简单的WSGI规范,但并不是全部,不过已经可以实现与多个框架的通信。相关的解释如下:

WSGI的初始化参数分别为主机ip,端口及需要调用的应用程序。

get_environ函数,从request_data中获取相应的CGI变量及WSGI变量,并传给应用程序。

make_server,阻塞监听端口。并接收客户端传来的消息交由应用程序进行处理,最后再将响应的结果打包,转成响应格式,交付给客户端。

make_response。将应用程序处理结果及请求头打包成响应,并关闭连接。

start_response,这个函数交给应用程序调用,其参数分别为状态码,响应头以及错误消息处理。

常见框架测试

这里是我的测试代码,通过我们自己写的服务器,可以成功的跑起weppy,flask及一个简单的满足WSGI规范的application。

# test.py

from wsgi_b import WSGIServer

from flask import Flask

from weppy import App

weppy_application = App(__name__)

flask_application = Flask(__name__)

@weppy_application.route("/")

def hello():

return "Hello World! from weppy"

@flask_application.route("/")

def hello():

return "Hello World! from flask"

def application(environ, start_response):

"""Simplest possible application object"""

status = '200 OK'

response_headers = [('Content-type', 'text/plain')]

start_response(status, response_headers)

return [b'Hello world! from simple app \n']

if __name__ == '__main__':

server = WSGIServer('127.0.0.1',8888,weppy_application).make_server()

但是这个服务器仍然还有许多需要完善的地方。不过不妨碍其做为学习WSGI协议时的补充。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/550841.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么把项目的数据上传到服务器,怎么把sql数据库上传到云服务器

怎么把sql数据库上传到云服务器 内容精选换一换本章介绍如何在管理控制台购买GaussDB(for openGauss)实例,并通过内网使用弹性云服务器连接GaussDB(for openGauss)实例。GaussDB(for openGauss)提供gsql工具帮助您在命令行下连接数据库,您需要提前创建一…

微信无连接服务器1.104,微信成语猜猜看太子少师答案汇总 太子少师1-104关攻略...

本文导航第1页: 太子少师第8页:翰林第2页:学童第9页:侍郎第3页:童生第10页:尚书第4页:秀才第11页:大学士第5页:举人第12页:御史第6页:贡士第13页&…

java反码_Java:二进制(原码、反码、补码)与位运算

一、二进制(原码、反码、补码)二进制的最高位是符号位(“0”代表正数,“1”代表负数);Java中没有无符号数;计算机以整数的补码进行运算;1. 原码:将一个整数转换成二进制表示以 int 类型为例,int类型占4个字…

java微博开发_【新手入门篇】新浪微博应用开发之Java入门篇

新浪微博应用开发之Java篇2012年11月11日星期日一、开发流程简介:2、 在以下地址创建一个应用,假设创建一个桌面应用:3、 进入管理中心,查看并记住应用ID和密码:4、 在“应用中心-高级信息”设置回调页面(设置后一般…

java 反射泛型方法_java基础之反射和泛型以及注解

java基础之反射和泛型以及注解泛型擦除泛型擦除: 泛型只在编译时期有效,编译后的字节码文件中不存在泛型信息。声明泛型集合,集合两端类型必须一致。类型也可以用包装类型,泛型的类型必须是引用类型,不能为基本类型。实…

java 16进制整数,Java将整数转换为十六进制整数

Im trying to convert a number from an integer into an another integer which, if printed in hex, would look the same as the original integer.For example:Convert 20 to 32 (which is 0x20)Convert 54 to 84 (which is 0x54)解决方案public static int convert(int n)…

flex java oracle_Flex使用Blazeds与Java交互及自定义对象转换详解-DATAGRID读取ORACLE数据...

一、建立Flex与Java交互的工程。本文中讲到的交互是利用Blazeds的,因为这个是免费的,呵呵,我是穷人。首先就是去下载Blazeds的压缩包,这个可以从官网或者CSDN、JavaEye上下到。解压缩这个包,将里面的Blazeds.war解压&a…

java重定向cookie_response请求转发和重定向,cookie

一.response:响应对象 提供的方法: void addCookie(Cookie cookie);服务端向客户端增加一个cookie对象 void sendRedirect(String location) throws IOExcetion:页面跳转的一种方法 void setContentType(String type):设置服务端响应的编码 示例重定向: login.jsp—->che…

linux java 文件夹创建失败_Linux文件夹文件创建、删除

Linux删除文件夹命令linux删除目录很简单,很多人还是习惯用rmdir,不过一旦目录非空,就陷入深深的苦恼之中,现在使用rm -rf命令即可。直接rm就可以了,不过要加两个参数-rf 即:rm -rf 目录名字删除目录、文件…

python zip函数转制_Python zip函数及用法

zip() 函数是 Python 内置函数之一,它可以将多个序列(列表、元组、字典、集合、字符串以及 range() 区间构成的列表)“压缩”成一个 zip 对象。所谓“压缩”,其实就是将这些序列中对应位置的元素重新组合,生成一个个新的元组。和 Python 3.x …

python中sin函数在哪个模块中_python标准库math中sin函数的参数单位是角度吗_弧度 - math.h中的三角函数,参数是...

在数学和物理中,弧度是角的度量单位。它是由国际单位制导出的单位,单位缩写是rad。定义1:弧长等于半径的弧,其所对的圆心角为1弧度。(即两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段弧。当这段弧长正好等于…

java jvm 加载_Jvm是如何加载Java类的?

前言我们知道Java语言的类型可以分为两大类:基本类型「primitive types」和引用类型「reference types」.对于Java的基本数据类型是Java虚拟机定义好的.至于另一大类「引用数据类型」, Java将其分为四种类型:类接口数组泛型参数由于「泛型参数」在编译时期会被擦除, 因此Java虚…

python查找文件内容_python自定义查找文件内容

#!/usr/bin/env python#coding:utf8#此脚本为查找递归目录下所有文件匹配的内容import os,sys,tabdef paths(path):list_pathos.walk(path)all_file[]for p,d,fl in list_path:for f in fl:pfileos.path.join(p,f)if os.path.isdir(pfile):paths(pfile)all_file.append(pfile)r…

derby mysql 同步_[Derby]数据库操作说明

1. 创建新数据库connect jdbc:derby:mydb;createtrue;ij> connect jdbc:derby:mydb;createtrue;ij>将在当前目录下建立数据库目录 mydb;如果已经存在数据库 mydb,则提示:警告 01J01:未创建数据库"mydb",而是建立到现有数据库…

lfu算法实现java_LFU五种实现方式,从简单到复杂

前言最近刷力扣题,对于我这种 0 基础来说,真的是脑壳疼啊。这个月我估计都是中等和困难题,没有简单题了。幸好,力扣上有各种大牛给写题解。看着他们行云流水的代码,真的是羡慕不已。让我印象最深刻的就是人称 “甜姨”…

java控制台高级_K9s Kubernetes的高级控制台

K9s Kubernetes CLI,以个性化方式管理您的集群!参考:Why K9s?首先我们得面对现实——Kubernetes及其周边生态系统的运营方式正变得越来越复杂。集群管理需要大量工具加以配合。当我在集群之上进行日常运营时,和大家一样…

java redis id生成器_基于redis的分布式ID生成器

项目地址基于redis的分布式ID生成器。准备首先,要知道redis的EVAL,EVALSHA命令:原理利用redis的lua脚本执行功能,在每个节点上通过lua脚本生成唯一ID。生成的ID是64位的:使用41 bit来存放时间,精确到毫秒&a…

Java自动切表_java_Mybatis自动创建表和更新表结构,最近有小伙伴问我mybatis有没有 - phpStudy...

Mybatis自动创建表和更新表结构最近有小伙伴问我mybatis有没有自动创建表结构的功能,因为他们之前一直使用hibernate用习惯了,理所当然的认为,在实体类上配置下注解或者写写映射文件,系统启动后就可以自动创建表。我只能很遗憾的告…

java byte 正数最大_关于JAVA中Byte类型的取值范围的推论(*零为正数,-128在计算机中的表示方法...)...

先看一段推理124:0111 1100  -124:1000 0100125:0111 1101  -125:1000 0011126:0111 1110  -126:1000 0010127:0111 1111  -127:1000 0001128:0 1000 0000 -128:1000 0000从上述过程对比可以看出:128是在127的基础上加1得到,但是会造成溢出(java中byte的大小为一个字…

java不同类间调用数组_请问:JAVA中两个类中的方法都需要调用另一个类的数组进行对数组的初始化和调用。...

匿名用户1级2013-09-14 回答import java.util.ArrayList;import java.util.Iterator;import java.util.List;/*** 测试类* author Administrator**/public class Test02 {public static void main(String[] args) {MovieStore movieStore new MovieStore();DVDManage.initDVD(…