一、55_长连接
"""单进程-单线程-非阻塞-长连接"""
import socket
import re
def service_client(new_socket, request):
"""为客户端返回数据"""
# 1、接收浏览器发送过来的请求, 即http请求
# -->例GET /index.html HTTP/1.1 .....
# http://127.0.0.1:7890/
# http://127.0.0.1:7890/index.html
# request = new_socket.recv(1024).decode("utf-8")
"""【.decode:解码】"""
# print("-*-" * 20)
# print(request)
request_lines = request.splitlines()
print("")
print("-*-" *20)
print(request_lines)
# GET / HTTP/1.1
# GET /index.html HTTP/1.1
# get post put del.....
ret = re.match(r"[^/]+/([^ ]*)", request_lines[0])
file_name =""
if ret:
file_name = ret.group(1)
print("*" *50, file_name)
if file_name =="/":
file_name ='/inedx.html'
# 2、返回http格式的数据给浏览器
try:
# f = open("../buickmall/indexhtml", "rb")
f =open("../buickmall" + file_name, "rb")
"""【rb:二进制读】"""
except:
response ="HTTP/1.1 404 NOT FOUND\r\n"
response +="\r\n"
response +="---file not found---"
new_socket.send(response.encode("utf-8"))
"""如果网址或(进程)出错时该给用户显示什么"""
else:
html_content = f.read()
f.close()
# 2.2 准备给浏览器发送的数据 :Body
response_body += html_content
# 2.1 准备给浏览器发送的数据 :Header
response_header ="HTTP/1.1 200 OK\r\n"
response_header +="Content-Length:%d\r\n" %len(response_body)
response +="\r\n"
"""【\r\n: 换行(兼容所有的浏览器)】"""
response = response_header.encode("utf-8") + response_body
# 将Response发送给浏览器
new_socket.send(response)
# 关闭套接字
# new_socket.close()
def main():
"""用来整体控制"""
# 1、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
"""选择让服务端先关闭"""
# 2、绑定; 参数("任意服务端ip", 端口)
tcp_server_socket.bind(("", 7890))
# 3、变为监听的套接字; 【listen:最大链接数】
tcp_server_socket.listen(128)
tcp_server_socket.setblocking(False)# 套接字变为非阻塞
client_socket_list =list()
while True:
# 4、等待新客户端的连接; 变量【新客户端的套接字, 客户端的IP、端口】
try:
new_socket, client_addr = tcp_server_socket.accept()
except Exception as ret:
pass
else:
new_socket.setblocking(False)
client_socket_list.append(new_socket)
for client_socketin client_socket_list:
try:
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)
# 6、关闭监听套接字
tcp_server_socket.close(new_socket)
if __name__ =='__main__':
main()
二、56_epoll的原理
单进程服务器-epoll【Linux服务器采用方式】
1、共享内存
2、事件通知;而不是轮询
(答辩题)
三、55_长短链接
Tcp长连接和短连接(服务器一般都是长连接)
一、HTTP/1.0:【短连接】
为了获取一个新的数据需要, 每次都要三次握手四次挥手
二、HTTP/1.1:【长连接】
通过一个套接字获取多个数据
四、57_epoll实现http
"""epoll实现http"""
import socket
import re
import select
def service_client(new_socket, request):
"""为客户端返回数据"""
# 1、接收浏览器发送过来的请求, 即http请求
# -->例GET /index.html HTTP/1.1 .....
# http://127.0.0.1:7890/
# http://127.0.0.1:7890/index.html
# request = new_socket.recv(1024).decode("utf-8")
"""【.decode:解码】"""
# print("-*-" * 20)
# print(request)
request_lines = request.splitlines()
print("")
print("-*-" *20)
print(request_lines)
# GET / HTTP/1.1
# GET /index.html HTTP/1.1
# get post put del.....
ret = re.match(r"[^/]+/([^ ]*)", request_lines[0])
file_name =""
if ret:
file_name = ret.group(1)
print("*" *50, file_name)
if file_name =="/":
file_name ='/inedx.html'
# 2、返回http格式的数据给浏览器
try:
# f = open("../buickmall/indexhtml", "rb")
f =open("../buickmall" + file_name, "rb")
"""【rb:二进制读】"""
except:
response ="HTTP/1.1 404 NOT FOUND\r\n"
response +="\r\n"
response +="---file not found---"
new_socket.send(response.encode("utf-8"))
"""如果网址或(进程)出错时该给用户显示什么"""
else:
html_content = f.read()
f.close()
# 2.2 准备给浏览器发送的数据 :Body
response_body += html_content
# 2.1 准备给浏览器发送的数据 :Header
response_header ="HTTP/1.1 200 OK\r\n"
response_header +="Content-Length:%d\r\n" %len(response_body)
response +="\r\n"
"""【\r\n: 换行(兼容所有的浏览器)】"""
response = response_header.encode("utf-8") + response_body
# 将Response发送给浏览器
new_socket.send(response)
# 关闭套接字
# new_socket.close()
def main():
"""用来整体控制"""
# 1、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
"""选择让服务端先关闭"""
# 2、绑定; 参数("任意服务端ip", 端口)
tcp_server_socket.bind(("", 7890))
# 3、变为监听的套接字; 【listen:最大链接数】
tcp_server_socket.listen(128)
tcp_server_socket.setblocking(False)# 套接字变为非阻塞
# 创建一个epoll对象
epl = select.epoll()
# 将监听的套接字FD注册到epoll中
epl.register(tcp_server_socket.fileno(), select.EPOLLIN)
"""【register:注册【fileno():FD】"""
fd_event_dict =dict()
while True:
fd_event_list = epl.poll()
"""poll():默认会阻塞,数据到来时,os时件通知的方式告诉程序"""
# [(fd, event),(),.....] 【fd:文件描述符(是一个数字)】
# 参数fd: 套接字对应的文件描述符】
# 参数event: 此fd是什么事件; 标记这个东西是可以收的还是发的】
for fd, eventin fd_event_list:
# 4、等待新客户端的连接; 变量【新客户端的套接字, 客户端的IP、端口】
if fd== tcp_server_socket.fileno():
new_socket, client_addr = tcp_server_socket.accept()
"""调用accept前的东西,它得是一个套接字的对象"""
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:
# 5、为这个客户端服务
service_client(fd_event_dict[fd], recv_data)
else:
fd_event_dict[fd].close()
epl.unregister(fd)
del fd_event_dict[fd]
# 6、关闭监听套接字
tcp_server_socket.close(new_socket)
if __name__ =='__main__':
main()