一、55_長連接
"""單進(jìn)程-單線程-非阻塞-長連接"""
import socket
import re
def service_client(new_socket, request):
"""為客戶端返回數(shù)據(jù)"""
? ? # 1、接收瀏覽器發(fā)送過來的請求, 即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格式的數(shù)據(jù)給瀏覽器
? ? try:
# f = open("../buickmall/indexhtml", "rb")
? ? ? ? f =open("../buickmall" + file_name, "rb")
"""【rb:二進(jìn)制讀】"""
? ? except:
response ="HTTP/1.1 404 NOT FOUND\r\n"
? ? ? ? response +="\r\n"
? ? ? ? response +="---file not found---"
? ? ? ? new_socket.send(response.encode("utf-8"))
"""如果網(wǎng)址或(進(jìn)程)出錯時該給用戶顯示什么"""
? ? else:
html_content = f.read()
f.close()
# 2.2 準(zhǔn)備給瀏覽器發(fā)送的數(shù)據(jù) :Body
? ? ? ? response_body += html_content
# 2.1 準(zhǔn)備給瀏覽器發(fā)送的數(shù)據(jù) :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發(fā)送給瀏覽器
? ? ? ? new_socket.send(response)
# 關(guān)閉套接字
# new_socket.close()
def main():
"""用來整體控制"""
? ? # 1、創(chuàng)建套接字
? ? tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
"""選擇讓服務(wù)端先關(guān)閉"""
? ? # 2尿贫、綁定; 參數(shù)("任意服務(wù)端ip", 端口)
? ? tcp_server_socket.bind(("", 7890))
# 3、變?yōu)楸O(jiān)聽的套接字; 【listen:最大鏈接數(shù)】
? ? tcp_server_socket.listen(128)
tcp_server_socket.setblocking(False)# 套接字變?yōu)榉亲枞?/p>
? ? 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、為這個客戶端服務(wù)
? ? ? ? ? ? ? ? ? ? service_client(client_socket, recv_data)
else:
client_socket.close()
client_socket_list.remove(client_socket)
# 6细移、關(guān)閉監(jiān)聽套接字
? ? tcp_server_socket.close(new_socket)
if __name__ =='__main__':
main()
二搏予、56_epoll的原理
單進(jìn)程服務(wù)器-epoll【Linux服務(wù)器采用方式】
1、共享內(nèi)存
2弧轧、事件通知雪侥;而不是輪詢
(答辯題)
三、55_長短鏈接
Tcp長連接和短連接(服務(wù)器一般都是長連接)
一精绎、HTTP/1.0:【短連接】
為了獲取一個新的數(shù)據(jù)需要, 每次都要三次握手四次揮手
二速缨、HTTP/1.1:【長連接】
通過一個套接字獲取多個數(shù)據(jù)
四、57_epoll實(shí)現(xiàn)http
"""epoll實(shí)現(xiàn)http"""
import socket
import re
import select
def service_client(new_socket, request):
"""為客戶端返回數(shù)據(jù)"""
? ? # 1代乃、接收瀏覽器發(fā)送過來的請求, 即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格式的數(shù)據(jù)給瀏覽器
? ? try:
# f = open("../buickmall/indexhtml", "rb")
? ? ? ? f =open("../buickmall" + file_name, "rb")
"""【rb:二進(jìn)制讀】"""
? ? except:
response ="HTTP/1.1 404 NOT FOUND\r\n"
? ? ? ? response +="\r\n"
? ? ? ? response +="---file not found---"
? ? ? ? new_socket.send(response.encode("utf-8"))
"""如果網(wǎng)址或(進(jìn)程)出錯時該給用戶顯示什么"""
? ? else:
html_content = f.read()
f.close()
# 2.2 準(zhǔn)備給瀏覽器發(fā)送的數(shù)據(jù) :Body
? ? ? ? response_body += html_content
# 2.1 準(zhǔn)備給瀏覽器發(fā)送的數(shù)據(jù) :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發(fā)送給瀏覽器
? ? ? ? new_socket.send(response)
# 關(guān)閉套接字
# new_socket.close()
def main():
"""用來整體控制"""
? ? # 1、創(chuàng)建套接字
? ? tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
"""選擇讓服務(wù)端先關(guān)閉"""
? ? # 2搁吓、綁定; 參數(shù)("任意服務(wù)端ip", 端口)
? ? tcp_server_socket.bind(("", 7890))
# 3原茅、變?yōu)楸O(jiān)聽的套接字; 【listen:最大鏈接數(shù)】
? ? tcp_server_socket.listen(128)
tcp_server_socket.setblocking(False)# 套接字變?yōu)榉亲枞?/p>
# 創(chuàng)建一個epoll對象
? ? epl = select.epoll()
# 將監(jiān)聽的套接字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():默認(rèn)會阻塞,數(shù)據(jù)到來時,os時件通知的方式告訴程序"""
? ? ? ? # [(fd, event),(),.....] 【fd:文件描述符(是一個數(shù)字)】
# 參數(shù)fd: 套接字對應(yīng)的文件描述符】
# 參數(shù)event: 此fd是什么事件; 標(biāo)記這個東西是可以收的還是發(fā)的】
? ? ? ? for fd, eventin fd_event_list:
# 4、等待新客戶端的連接; 變量【新客戶端的套接字, 客戶端的IP堕仔、端口】
? ? ? ? ? ? if fd== tcp_server_socket.fileno():
new_socket, client_addr = tcp_server_socket.accept()
"""調(diào)用accept前的東西,它得是一個套接字的對象"""
? ? ? ? ? ? ? ? epl.register(new_socket.fileno(), select.EPOLLIN)
fd_event_dict[new_socket.fileno()] = new_socket
elif event == select.EPOLLIN:
# 判斷已經(jīng)連接的客戶端是否有數(shù)據(jù)發(fā)送過來
? ? ? ? ? ? ? ? recv_data = fd_event_dict[fd].recv(1024).decode("utf-8")
if recv_data:
# 5员咽、為這個客戶端服務(wù)
? ? ? ? ? ? ? ? ? ? service_client(fd_event_dict[fd], recv_data)
else:
fd_event_dict[fd].close()
epl.unregister(fd)
del fd_event_dict[fd]
# 6、關(guān)閉監(jiān)聽套接字
? ? tcp_server_socket.close(new_socket)
if __name__ =='__main__':
main()