模仿web服务器 - 多进程-多线程-协程实现-短连接非阻塞
# 导入socket
import socket
# 导入多线程
import threading
# 导入进程
import multiprocessing
# 导入协程工具包
import gevent
from gevent import monkey
# 导入正则表达式
import re
# 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块,必须执行的方法
monkey.patch_all()
def send_error(tcp_clinet_socket):
# 否则返回一个没有找到页面
response = "HTTP/1.1 404 NOT FOUND\r\n"
response += "\r\n"
response += "没有找到该页面"
tcp_clinet_socket.send(response.encode("gbk")) # 先发送头部
def getClientMsg(tcp_clinet):
# 获取客户端数据
msg_data = tcp_clinet.recv(1024).decode("utf-8")
# 解析数据GET /index.html HTTP/1.1
msg_list = msg_data.splitlines() # 将数据切割成序列
ret = re.match(r"[^/]+ (/[^ ]*)", msg_list[0])
file_name = None
if ret:
file_name = ret.group(1)
if file_name == "/":
file_name = "/index.html"
local_file_name = "." + file_name
response = "HTTP/1.1 200 OK\r\n"
response += "\r\n"
try:
f = open(local_file_name, "rb")
except:
send_error(tcp_clinet) # 没有找到页面发送次数据
else:
# 发送数据到客户端
tcp_clinet.send(response.encode("utf-8"))
tcp_clinet.send(f.read())
f.close()
else:
send_error(tcp_clinet) # 没有找到页面发送次数据
# 关闭客户端链接
tcp_clinet.close()
def main():
# 创建套接字
tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定ip和端口
tcp_socket_server.bind(("", 9999))
# 启动监听
tcp_socket_server.listen(128)
while True:
# 等待客户端链接
tcp_clinet, addr_clinet = tcp_socket_server.accept()
# 获取客户端信息,并返回信息
"""
# 单任务执行
getClientMsg(tcp_clinet)
"""
"""
# 使用多线程执行
p = threading.Thread(target=getClientMsg, args=(tcp_clinet,))
# 启动线程
p.start()
"""
"""
# 多进程使用执行
m = multiprocessing.Process(target=getClientMsg, args=(tcp_clinet,))
# 启动进程 m.start()
# 由于多任务是所有的执行代码都拷贝,所以这里也需要关闭一次
#tcp_clinet.close()
"""
# 使用协程执行,必须在要执行的协程代码头部运行monkey.patch_all()方法
gevent.spawn(getClientMsg, tcp_clinet)
if __name__ == "__main__":
main()
然后在浏览器访问127.0.0.1/index.html页面就可以访问了,前提是你本地有一个index.html页面
使用非阻塞长连接的模式来书写案例
# 导入socket
import socket
# 导入正则表达式
import re
import time
def send_error(tcp_clinet_socket):
# 否则返回一个没有找到页面
response = "HTTP/1.1 404 NOT FOUND\r\n"
response += "\r\n"
response += "没有找到该页面"
tcp_clinet_socket.send(response.encode("gbk")) # 先发送头部
# 在异常中需要关闭一下套接字,否则客户端不停的等待访问
tcp_clinet_socket.close()
def getClientMsg(tcp_clinet, msg_data):
# 解析数据GET /index.html HTTP/1.1
msg_list = msg_data.splitlines() # 将数据切割成序列
ret = re.match(r"[^/]+ (/[^ ]*)", msg_list[0])
file_name = None
if ret:
file_name = ret.group(1)
if file_name == "/":
file_name = "/index.html"
local_file_name = "." + file_name
try:
f = open(local_file_name, "rb")
except:
send_error(tcp_clinet) # 没有找到页面发送次数据
else:
# 发送数据到客户端
response_body = f.read()
response_header = "HTTP/1.1 200 OK\r\n"
# 加了这个头信息,是返回body长度,就不需要在关闭socket.close方法,客户端就知道了
response_header += "Content-Length:%d\r\n" % len(response_body)
response_header += "\r\n"
response = response_header.encode("utf-8") + response_body
print(response)
tcp_clinet.send(response)
f.close()
else:
send_error(tcp_clinet) # 没有找到页面发送次数据
def main():
# 创建套接字
tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定ip和端口
tcp_socket_server.bind(("", 9999))
# 启动监听
tcp_socket_server.listen(128)
# 将套接字改为非阻塞
tcp_socket_server.setblocking(False)
# 定义一个序列储存客户端链接
client_socket_list = list()
while True:
time.sleep(1)
# 等待客户端链接,这里由于获取不到链接所以需要使用异常处理
try:
tcp_clinet, addr_clinet = tcp_socket_server.accept()
except:
print("没有链接=======")
else:
# 获取客户端信息,并返回信息
client_socket_list.append(tcp_clinet)
# 如果有客户端链接,将客户端套接字也设置为非阻塞
tcp_clinet.setblocking(False)
# 循环所有客户端套接字来接收数据
for client in client_socket_list:
try:
data = client.recv(1024).decode("utf-8")
except:
print("没有接收到数据======")
else:
if data:
getClientMsg(client, data)
else:
# 关闭客户端套接字资源
client.close()
# 删除套接字
client_socket_list.remove(client)
if __name__ == "__main__":
main()
作者:阿超
原创公众号:『Python日常笔记』,专注于 Python爬虫等技术栈和有益的程序人生,会将一些平时的日常笔记都慢慢整理起来,也期待你的关注和阿超一起学习,公众号回复【csdn】优质资源。