python之动态服务器初练习
首先是简单的应用程序框架
实现原理是根据动态资源不同返回的数据信息
浏览器–1.请求动态资源–服务器–2.wsgi协议调用接口–应用程序框架(Django/flask…)
3.web框架通过引用服务器的方法,设置返回的状态和头信息
4.调用返回,服务器会保存设置信息
5.框架查询数据库,生成动态的页面
6.把生成的动态数据返回给服务器
7.web服务器将数据返回给客户端
中间是必须写接口
以下是实例类实现,类名为application.py(名字和导包有关,用到__import__)
def app(environ, start_response):
'''
:param environ: 跟http请求相关的数据(用户请求路径,请求头),必须是个字典
:param start_response: 函数引用(由服务器提供),作用:设置状态和响应头
start_response这个函数有两个参数,第一个参数表示状态,第二个参数表示响应
的头部(列表类型),写在服务器上但是框架上调用
:return:返回值是网页的内容
'''
# 根据不同的请求路径返回不同内容
request_path = environ['path']
if request_path == 'index.py':
# return_str是我们要返回的网页内容
return_str = '###########这是主页面##############'
# elif request_path == 'login.py' # 这里可以写自己想要区分的内容
else:
return_str = '这是动态服务器返回的网页内容'
# 这个是调用服务器的方法,这里传入内容
# 参数一表示状态,参数二表示请求内容
start_response('200 ok'. [('name','paul'), ('age','18)])
return return_str
服务器类:
import socket
import threading
import sys
import re
import chardet
# 这是服务器类可以静态也可以动态调用
class MyServer(object):
def __init__(self, port, app):
'''
:param port:这是端口
:param app:这个是调用应用框架的方法
'''
self.app = app
# 初始化创建服务器套接字
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(('', port))
# 要传输的数据设为属性方便使用
self.response_data = None
# 启动服务器
def start(self):
# 这是监听
self.server_socket.listen()
while True:
# 直接拆包accept()函数得到的内容,第一个是服务套接字,第二个是地址信息
client_socket, address_info = self.server_socket.accept()
# 创建一个线程处理客户端的请求
client_thread = threading.Thread(target=self.handle_client, args=(client_socket,))
client_thread.start()
def handle_client(self, client_socket):
# 客户服务
data = client_socket.recv(1024).decode('utf-8')
data_list = data.split('\r\n')
first_line = data_list[0]
if not first_line:
#如果没有数据就关闭
client_socket.close()
return
try:
ret = re.search('GET (/.*?) HTTP1.1', first_line)
request_path = ret.group(1)
except:
# 如果没有就给一个默认值
request_path = '/'
# 判断结尾是否.py 是则进入动态判断
if request_path.endswith('.py'):
response_body = self.app(environ={'path':request_path}, self.start_response)
response = self.response_data + response_body
client_socket.send(response.encode('gbk'))
# 不是就静态判断:
else:
# 这是默认为正确的状态码,在错误页面改成错误内容
status_code = '200 ok'
if request_path == '/' or request_path == '/index.html':
with open('index.html') as f:
response_body = f.read()
elif request_path == '/login.html' :
with open('cat_head.png' 'rb') as f: # 这是传输图片内容
response_body = f.read() # 注意这里是二进制数据
else:
# 这是出错页面
status_code = '404 not found'
with open('err.html') as f:
response_body = f.read()
# 这里写请求头内容
response_header = 'HTTP1.1 ' + status_code + '\r\n'
response_header += '\r\n' # 这里是静态所以没有内容直接加'\r\n'
try:
chardet.detect(response_body) # 判断是否是二进制
response_header = response_header.encode('utf-8')
except:
response_header = response_header.encode('utf-8')
response_body = response_body.encode('utf-8')
finally:
sent_count = 0 # 这是已经发送数据大小
self.response_data = response_header + response_body # 这是要发送的所有数据
while sent_count < len(self.response_data):
# 这里写的是如果没发送完继续传输,
now_sendsize = client_socket.send(self.response_data[sent_count:]) # 这是现在发送的数据大小
sent_count += now_sendsize
# 传输完关闭客户端套接字
client_socket.close()
# 动态判断用的
def start_response(self, status, header_list):
response_header_firstline = 'HTTP1.1' + status + '\r\n'
response_header = ''
# 传入的header_list是header键值,直接拆包
for header_key, header_value in header_list:
response_header += header_key + header_value +'\r\n'
self.response_data = response_header_firstline + response_header +'\r\n'
def main():
# 实现主要逻辑
try:
# 这个是用端口来启动py文件
port = sys.argv[1] # argv是个列表第一个参数是当前执行文件名,之后是你输入的值
frame_name = sys.argv[2]
except:
# 当没有正确输入的时候给个默认值
print('没有正确输入')
port = 8000
frame_name = 'application:app'
finally:
frame_list = frame_name.split(':') # 端口输入内容为application:app,所以要切割
modle_name = frame_list[0] # 要导入包的名字
app_name = frame_list[1] # 方法名
ret = __import__(modle_name) # 这是根据string导包的方式
app = getattr(ret, app_name) # 这个方法得到要导方法
my_server = MyServer(port=int(port), app=app)
my_server.start()
if __name__ == '__main__':
main()