前言:此为python学习笔记,如有错误或争议敬请发送到邮箱 zytwell321@163.com ,万分感谢!
1.长连接实现http服务
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# author:Torry Zhang
# contact: zytwell321@163.com
# datetime:2020/8/19 21:32
# software: PyCharm
"""
单进程-单线程-非阻塞-长连接
"""
import socket
import re
def service_client(new_socket, request_data):
""" 为客户端提供返回数据 """
# 1.接收客户端浏览器发送过来的数据,如:GET / HTTP/1.1
# ...
# request_data = new_socket.recv(1024).decode("utf-8")
print(request_data)
# 将请求结果分割成列表
request_lines = request_data.splitlines()
# print(">" * 20)
# print(request_lines)
# GET /html/index.html HTTP/1.1
# GET / HTTP/1.1
# 请求类型:GET POST DELETE ...
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1) # 提取请求路径
print("*" * 30, file_name)
if file_name == "/":
file_name = "/index.html"
# 2.返回http格式信息给客户端浏览器
try:
f = open("./html" + file_name, "rb") # 打开url访问路径文件
except:
response = "HTTP/1.1 404\r\n"
response += "\r\n"
response += "<h1>404</h1>\r\nFILE NOT FOUND"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 组装返回给浏览器的数据-- header
print(" " * 50)
response_body = html_content
response_header = "HTTP/1.1 200 OK\r\n"
response_header += "Content-Length: %d\r\n" % len(response_body) # 告诉浏览器报文发送完毕
response_header += "\r\n"
response = response_header.encode("utf-8") + response_body
# 返回给浏览器内容:报文头header+body
new_socket.send(response)
# 3.关闭套接字
# new_socket.close() # 连接后关闭,将是短连接。注释掉,变成长连接,客户端自动发起请求
def main():
""" tcp-socket 流程控制 """
# 1.创建套接字
tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置当前服务器先close 即服务器端4次挥手后资源能够立即释放。保证下次程序启用时,可以立即启用端口
tcp_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2.绑定
tcp_socket_server.bind(("",8080))
# 3.变为监听套接字
tcp_socket_server.listen(128)
tcp_socket_server.setblocking(False) # 将套接字变成非阻塞形式
client_socket_list = list()
while True:
# 4.等待新客户端连接
try: # 非阻塞形式,没有客户端连接会报异常。所以增加try except语句
new_socket, client_addr = tcp_socket_server.accept()
except Exception as ret:
pass
else:
new_socket.setblocking(False)
client_socket_list.append(new_socket)
# 遍历连接服务的client列表
for client_socket in client_socket_list:
try: # 非阻塞形式,没有客户端连接会报异常。所以增加try except语句
recv_data = client_socket.recv(1024).decode("utf-8")
except Exception as ret:
pass
else:
if recv_data:
# 5.为客户端提供服务
service_client(client_socket, recv_data)
else: # 如果客户端没有发送数据,则关闭连接
client_socket.close()
client_socket_list.remove(client_socket)
# 5.为客户端提供服务
# service_client(new_socket)
tcp_socket_server.close()
if __name__ == "__main__":
main()
2.epoll实现http服务
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# author:Torry Zhang
# contact: zytwell321@163.com
# datetime:2020/8/20 13:19
# software: PyCharm
"""
epoll采用基于事件通知的方式
备注:epoll()只试用于Linux 2.5+,并不支持其他系统
"""
import socket
import re
import select
def service_client(new_socket, request_data):
""" 为客户端提供返回数据 """
# 1.接收客户端浏览器发送过来的数据,如:GET / HTTP/1.1
# ...
# request_data = new_socket.recv(1024).decode("utf-8")
print(request_data)
# 将请求结果分割成列表
request_lines = request_data.splitlines()
# print(">" * 20)
# print(request_lines)
# GET /html/index.html HTTP/1.1
# GET / HTTP/1.1
# 请求类型:GET POST DELETE ...
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1) # 提取请求路径
print("*" * 30, file_name)
if file_name == "/":
file_name = "/index.html"
# 2.返回http格式信息给客户端浏览器
try:
f = open("./html" + file_name, "rb") # 打开url访问路径文件
except:
response = "HTTP/1.1 404\r\n"
response += "\r\n"
response += "<h1>404</h1>\r\nFILE NOT FOUND"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 组装返回给浏览器的数据-- header
print(" " * 50)
response_body = html_content
response_header = "HTTP/1.1 200 OK\r\n"
response_header += "Content-Length: %d\r\n" % len(response_body) # 告诉浏览器报文发送完毕
response_header += "\r\n"
response = response_header.encode("utf-8") + response_body
# 返回给浏览器内容:报文头header+body
new_socket.send(response)
# 3.关闭套接字
# new_socket.close() # 连接后关闭,将是短连接。注释掉,变成长连接,客户端自动发起请求
def main():
""" tcp-socket 流程控制 """
# 1.创建套接字
tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置当前服务器先close 即服务器端4次挥手后资源能够立即释放。保证下次程序启用时,可以立即启用端口
tcp_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2.绑定
tcp_socket_server.bind(("",8080))
# 3.变为监听套接字
tcp_socket_server.listen(128)
tcp_socket_server.setblocking(False) # 将套接字变成非阻塞形式
# 创建一个epoll对象
epl = select.epoll()
# 将监听套接字对应的fd注册到epoll中
epl.register(tcp_socket_server.fileno(), select.EPOLLIN)
fd_event_dict = dict()
while True:
# 默认会阻塞,知道os检测到数据到来 通过事件通知方式,告诉这个程序,此时才会解锁
fd_event_list = epl.poll()
# [(fd, event), (套接字对应的文件描述符。这个文件描述符到底是什么事件。例如,可以调用decv接收)]
for fd, event in fd_event_list:
# 4.等待新客户端连接
if fd == tcp_socket_server.fileno():
new_socket, client_addr = tcp_socket_server.accept()
# 监听到套接字对应的客户端fd注册到epoll中
epl.register(new_socket.fileno(), select.EPOLLIN)
fd_event_dict[new_socket.fileno()] = new_socket
elif event == select.EPOLLIN:
# 判断已经连接的客户端是否有数据发送过来
recv_data = fd_event_dict[fd].recv(1024).decode("utf-8")
if recv_data:
service_client(fd_event_dict[fd], recv_data)
else:
fd_event_dict[fd].close()
epl.unregister(fd)
del fd_event_dict[fd]
tcp_socket_server.close()
if __name__ == "__main__":
main()
高并发执行响应结果: