日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python server send event_[Python之路] 多种方式实现并发Web Server

發布時間:2025/4/5 python 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python server send event_[Python之路] 多种方式实现并发Web Server 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

下面我們使用Python來實現并發的Web Server,其中采用了多進程、多線程、協程、單進程單線程非阻塞的方式。

一、使用子進程來實現并發Web Server

importsocketimportreimportmultiprocessingdefhandle_request(new_socket):#接收請求

recv_msg = ""recv_msg= new_socket.recv(1024).decode("utf-8")if recv_msg == "":print("recv null")

new_socket.close()return

#從請求中解析出URI

recv_lines =recv_msg.splitlines()print(recv_lines.__len__())#使用正則表達式提取出URI

ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])ifret:#獲取URI字符串

file_name = ret.group(1)#如果URI是/,則默認返回index.html的內容

if file_name == "/":

file_name= "/index.html"

try:#根據請求的URI,讀取相應的文件

fp = open("." + file_name, "rb")except:#找不到文件,響應404

response_msg = "HTTP/1.1 404 NOT FOUND\r\n"response_msg+= "\r\n"response_msg+= "

----file not found----

"new_socket.send(response_msg.encode("utf-8"))else:

html_content=fp.read()

fp.close()#響應正確 200 OK

response_msg = "HTTP/1.1 200 OK\r\n"response_msg+= "\r\n"

#返回響應頭

new_socket.send(response_msg.encode("utf-8"))#返回響應體

new_socket.send(html_content)#關閉該次socket連接

new_socket.close()defmain():#創建TCP SOCKET實例

tcp_server_socket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)## 設置重用地址

#tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

#綁定地址(默認本機IP)和端口

tcp_server_socket.bind(("", 7890))#監聽

tcp_server_socket.listen(128)#循環接收客戶端連接

whileTrue:

new_socket, client_addr=tcp_server_socket.accept()#啟動一個子進程來處理客戶端的請求

sub_p = multiprocessing.Process(target=handle_request, args=(new_socket,))

sub_p.start()#這里要關閉父進程中的new_socket,因為創建子進程會復制一份new_socket給子進程

new_socket.close()#關閉整個SOCKET

tcp_server_socket.close()if __name__ == "__main__":

main()

我們使用進程來實現并發的Web Server,也就是將Accept到new_socket傳遞給子進程去處理,處理函數還是handle_request。

但是這里注意,子進程會從父進程中將所有的變量進行拷貝,也就是說父進程和子進程中各有一份new_socket,而在Linux下,socket對應的也是一個文件描述符,而這兩個new_socket實際上是指向同一個fd的。所以我們將new_socket交給子進程后,父進程就可以馬上關閉自己的new_socket了,當子進程服務完畢后,關閉子進程中的new_socket,這樣對應的FD才會正真關閉,此時才會觸發四次揮手。所以父進程代碼中藍色部分new_socket.close()非常重要。

二、使用線程來實現并發Web Server

在第一節中,我們使用進程來實現并發,但是進程對資源消耗很大,一般不推薦使用。所以這里我們使用線程來實現并發,很簡單,我們將?multiprocessing.Process 替換為 threaing.Thread就可以了:

importsocketimportreimportthreadingdefhandle_request(new_socket):#接收請求

recv_msg = ""recv_msg= new_socket.recv(1024).decode("utf-8")if recv_msg == "":print("recv null")

new_socket.close()return

#從請求中解析出URI

recv_lines =recv_msg.splitlines()print(recv_lines.__len__())#使用正則表達式提取出URI

ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])ifret:#獲取URI字符串

file_name = ret.group(1)#如果URI是/,則默認返回index.html的內容

if file_name == "/":

file_name= "/index.html"

try:#根據請求的URI,讀取相應的文件

fp = open("." + file_name, "rb")except:#找不到文件,響應404

response_msg = "HTTP/1.1 404 NOT FOUND\r\n"response_msg+= "\r\n"response_msg+= "

----file not found----

"new_socket.send(response_msg.encode("utf-8"))else:

html_content=fp.read()

fp.close()#響應正確 200 OK

response_msg = "HTTP/1.1 200 OK\r\n"response_msg+= "\r\n"

#返回響應頭

new_socket.send(response_msg.encode("utf-8"))#返回響應體

new_socket.send(html_content)#關閉該次socket連接

new_socket.close()defmain():#創建TCP SOCKET實例

tcp_server_socket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)## 設置重用地址

#tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

#綁定地址(默認本機IP)和端口

tcp_server_socket.bind(("", 7890))#監聽

tcp_server_socket.listen(128)#循環接收客戶端連接

whileTrue:

new_socket, client_addr=tcp_server_socket.accept()#啟動一個線程來處理客戶端的請求

t = threading.Thread(target=handle_request, args=(new_socket,))

t.start()#關閉整個SOCKET

tcp_server_socket.close()if __name__ == "__main__":

main()

我們發現,除了將子進程的創建過程替換成了線程的創建過程,后面的new_socket.close()也被刪除了,這是因為線程是公用進程資源的,new_socket不會被復制,所以socket對應的FD,只有一個new_socket指向他。

如果此時我們仍然在這里關閉new_socket,那么在線程再使用new_socket就會報錯。如下信息:

Exception in thread Thread-5:

Traceback (most recent call last):

File "D:\Anaconda3_530\lib\threading.py", line 917, in _bootstrap_inner

self.run()

File "D:\Anaconda3_530\lib\threading.py", line 865, in run

self._target(*self._args, **self._kwargs)

File "D:/pycharm_project/leo1127/thread_web_server.py", line 44, in handle_request

new_socket.send(response_msg.encode("utf-8"))

OSError: [WinError 10038] 在一個非套接字上嘗試了一個操作。

三、使用協程來實現并發Web Server

使用進程和線程來實現的并發Web Server,當并發訪問量很大時,資源消耗都很高。所以這里使用協程來實現并發服務器。

importsocketimportreimportgeventfrom gevent importmonkey

monkey.patch_all()defhandle_request(new_socket):#接收請求

recv_msg = ""recv_msg= new_socket.recv(1024).decode("utf-8")if recv_msg == "":print("recv null")

new_socket.close()return

#從請求中解析出URI

recv_lines =recv_msg.splitlines()print(recv_lines.__len__())#使用正則表達式提取出URI

ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])ifret:#獲取URI字符串

file_name = ret.group(1)#如果URI是/,則默認返回index.html的內容

if file_name == "/":

file_name= "/index.html"

try:#根據請求的URI,讀取相應的文件

fp = open("." + file_name, "rb")except:#找不到文件,響應404

response_msg = "HTTP/1.1 404 NOT FOUND\r\n"response_msg+= "\r\n"response_msg+= "

----file not found----

"new_socket.send(response_msg.encode("utf-8"))else:

html_content=fp.read()

fp.close()#響應正確 200 OK

response_msg = "HTTP/1.1 200 OK\r\n"response_msg+= "\r\n"

#返回響應頭

new_socket.send(response_msg.encode("utf-8"))#返回響應體

new_socket.send(html_content)#關閉該次socket連接

new_socket.close()defmain():#創建TCP SOCKET實例

tcp_server_socket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)## 設置重用地址

#tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

#綁定地址(默認本機IP)和端口

tcp_server_socket.bind(("", 7890))#監聽

tcp_server_socket.listen(128)#循環接收客戶端連接

whileTrue:

new_socket, client_addr=tcp_server_socket.accept()#啟動一個協程來處理客戶端的請求

gevent.spawn(handle_request, new_socket)#關閉整個SOCKET

tcp_server_socket.close()if __name__ == "__main__":

main()

使用gevent來實現協程,并發處理請求。

四、使用單進程單線程非阻塞模擬并發(非并行)

前面我們使用的多進程和多線程來處理并發,是因為socket.recv()是阻塞的,每次accept一個連接,就需要交給一個新的進程或線程去處理,從而不影響下一個socket連接。

但是我們可以通過單進程單線程和非阻塞的方式來完成并發socket的處理:

importsocketdefmain():#創建TCP SOCKET實例

tcp_server_socket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)## 設置重用地址

#tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

#綁定地址(默認本機IP)和端口

tcp_server_socket.bind(("", 7890))#監聽

tcp_server_socket.listen(128)#將accept設置為非阻塞,這里設置一次,后面不管調多少次accept都是非阻塞的

tcp_server_socket.setblocking(False)#定義一個列表,將每次連接的socket加入該列表

client_socket_list =list()#循環接收客戶端連接

whileTrue:try:

new_socket, client_addr=tcp_server_socket.accept()exceptException as ret:#當沒有客戶端鏈接的時候,拋出異常

pass

else:#當有客戶端鏈接的時候

#將new_socket.recv()設置為非阻塞的

new_socket.setblocking(False)#將new_socket加入列表

client_socket_list.append(new_socket)#遍歷socket列表,檢查每一個socket是否有數據到達,或者客戶端是否斷開

for client_socket inclient_socket_list:try:

recv_content=client_socket.recv(1024)exceptException as ret:#異常,表示該客戶端沒有發數據過來

pass

else:#正常,表示客戶端發了數據,或者客戶端斷開連接(斷開連接會導致recv正常返回)

ifrecv_content:#有數據,調用請求處理代碼

print("處理請求")else:#客戶端斷開時,服務器也關閉連接

client_socket.close()#將已關閉的鏈接提出列表

client_socket_list.remove(client_socket)#關閉整個SOCKET

tcp_server_socket.close()

上面代碼只有主干部分(省略了請求處理部分),主要是說明在單進程單線程情況下,如何將accept和recv分開,并且都用非阻塞的方式來處理,這樣每次查看是否有客戶端鏈接進來的時候,都會去檢查所有已鏈接的socket是否有數據發送過來。

注意:socket.recv(1024)一定要給參數,讀取的字節數。否則會一直報異常。

在這種方式中,我們使用單進程單線程模擬了并發處理socket連接的功能,但這些socket連接的處理不是并行的。當一個socket處理數據時間比較長時,也會造成整個程序的等待。

五、短連接和長連接

在第四節中,我們使用單進程單線程非阻塞的形式實現了并發處理socket連接。其中省略了實際處理的部分,我們將其補充上:

importsocketimporttimeimportredefhandle_request(new_socket, recv_msg):#從請求中解析出URI

recv_lines =recv_msg.splitlines()#使用正則表達式提取出URI

ret = re.match(r"[^/]+(/[^ ]*)", recv_lines[0])ifret:#獲取URI字符串

file_name = ret.group(1)#如果URI是/,則默認返回index.html的內容

if file_name == "/":

file_name= "/index.html"

try:#根據請求的URI,讀取相應的文件

fp = open("." + file_name, "rb")except:#找不到文件,響應404

response_msg = "HTTP/1.1 404 NOT FOUND\r\n"response_msg+= "\r\n"response_msg+= "

----file not found----

"new_socket.send(response_msg.encode("utf-8"))else:

html_content=fp.read()

fp.close()

response_body=html_content#響應正確 200 OK

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#返回響應數據

new_socket.send(response)defmain():#創建TCP SOCKET實例

tcp_server_socket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)## 設置重用地址

#tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

#綁定地址(默認本機IP)和端口

tcp_server_socket.bind(("", 7890))#監聽

tcp_server_socket.listen(128)#將accept設置為非阻塞,這里設置一次,后面不管調多少次accept都是非阻塞的

tcp_server_socket.setblocking(False)#定義一個列表,將每次連接的socket加入該列表

client_socket_list =list()#循環接收客戶端連接

whileTrue:

time.sleep(0.5)try:

new_socket, client_addr=tcp_server_socket.accept()exceptException as ret:#當沒有客戶端鏈接的時候,拋出異常

pass

else:print("一個新的客戶端連接。。。。")#當有客戶端鏈接的時候

#將new_socket.recv()設置為非阻塞的

new_socket.setblocking(False)#將new_socket加入列表

client_socket_list.append(new_socket)print(client_socket_list.__len__())#遍歷socket列表,檢查每一個socket是否有數據到達,或者客戶端是否斷開

for client_socket inclient_socket_list:try:

recv_content= client_socket.recv(1024).decode("utf-8")exceptException as ret:#異常,表示該客戶端沒有發數據過來

pass

else:#正常,表示客戶端發了數據,或者客戶端斷開連接(斷開連接會導致recv正常返回)

ifrecv_content:#有數據,調用請求處理代碼

handle_request(client_socket, recv_content)else:#recv正常返回,且數據為空,表示客戶端斷開了鏈接

#將該socket踢出列表

client_socket_list.remove(client_socket)#服務器也關閉連接

client_socket.close()#關閉整個SOCKET

tcp_server_socket.close()if __name__ == "__main__":

main()

特別注意的是,在請求處理函數handle_request中,我們將請求內容作為參數一并傳遞進去。然后在返回200 OK的時候,在響應頭中添加了Content-Length字段,這個字段用于告訴客戶端,此次發送的響應體有多大。當客戶端收完指定大小的數據,就認為這次服務器發送的數據已經發送完畢。他就可以繼續發送下一個新的請求。

在handle_request中可以看到,new_socket.close()已經被刪除,也就是說服務器不會自動關閉連接,而直到客戶端斷開連接之前,服務器都保持長連接。斷開連接由客戶端來發起。

總結

以上是生活随笔為你收集整理的python server send event_[Python之路] 多种方式实现并发Web Server的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。