Python Socket編程詳解
背景
關(guān)于Python Socket編程,首先需要了解幾個(gè)計(jì)算機(jī)網(wǎng)絡(luò)的知識,通過以下的幾個(gè)問題,有助于更好的理解Socket編程的意義,以及整個(gè)框架方面的知識:
TCP和UDP協(xié)議本質(zhì)上的區(qū)別?
TCP協(xié)議,面向連接,可靠,基于字節(jié)流的傳輸層通信協(xié)議;UDP協(xié)議無連接,不可靠,基于數(shù)據(jù)包的傳輸層協(xié)議。
TCP協(xié)議在建立連接的過程需要經(jīng)歷三次握手,斷開連接則需要經(jīng)歷四次揮手,而這建立連接的過程增加了傳輸過程中的安全性。
而建立連接的過程則會消耗系統(tǒng)的資源,消耗更多的時(shí)間,而相比較UDP協(xié)議傳輸過程則不會出現(xiàn)這種問題。
總結(jié)來講,基于TCP協(xié)議傳輸,需要不斷的確認(rèn)對方是否收到信息,從而建立連接(確認(rèn)過程次數(shù)有限制,即三次握手),UDP協(xié)議傳輸則
不需要確認(rèn)接收端是否收到信息,只需要將信息發(fā)給對方。
TCP/IP協(xié)議棧、HTTP協(xié)議、Socket之間的區(qū)別和聯(lián)系?
TCP/IP協(xié)議棧就是一系列網(wǎng)絡(luò)協(xié)議,可以分為四層模型來分析:應(yīng)用層、傳輸層、網(wǎng)絡(luò)層、鏈路層;
HTTP協(xié)議(超文本傳輸協(xié)議)就是在這一協(xié)議棧中的應(yīng)用層協(xié)議;HTTP協(xié)議簡單來說,它的作用就是規(guī)范數(shù)據(jù)的格式,讓程序能夠方便的識別,并且收發(fā)雙方都需要遵循同樣的協(xié)議格式進(jìn)行數(shù)據(jù)傳輸。(應(yīng)用層的協(xié)議也和HTTP協(xié)議的作用類似,不一樣的是定義不同的數(shù)據(jù)格式。)
Socket可以理解為TCP/IP協(xié)議棧提供的對外的操作接口,即應(yīng)用層通過網(wǎng)絡(luò)協(xié)議進(jìn)行通信的接口。Socket可以使用不同的網(wǎng)絡(luò)協(xié)議進(jìn)行端對端的通信;
TCP Socket服務(wù)器的通信過程?
Server端:
建立連接(socket()函數(shù)創(chuàng)建socket描述符、bind()函數(shù)綁定特定的監(jiān)聽地址(ip+port)、listen()函數(shù)監(jiān)聽socket、accept()阻塞等待客戶端連接)
數(shù)據(jù)交互(read()函數(shù)阻塞等待客戶端發(fā)送數(shù)據(jù)、write()函數(shù)發(fā)送給客戶端數(shù)據(jù))
Client端:
建立連接(socket()函數(shù)創(chuàng)建socket描述符、connect()函數(shù)向指定的監(jiān)聽地址發(fā)送連接請求)
數(shù)據(jù)交互(wirte()函數(shù)發(fā)送服務(wù)端數(shù)據(jù)、read()函數(shù)足阻塞等待接受服務(wù)端發(fā)送的數(shù)據(jù))
socket和websocket之間的聯(lián)系?
webosocket是一種通信協(xié)議,不同于HTTP請求,客戶端請求服務(wù)端資源,服務(wù)端響應(yīng)的通信過程;websocket允許服務(wù)端主動
向客戶端推送消息,同時(shí)做到客戶端和服務(wù)端雙向通訊的協(xié)議。(具體底層原理有待后面實(shí)踐,暫時(shí)未接觸)
HTTP,WSGI協(xié)議的聯(lián)系和區(qū)別?
HTTP協(xié)議(超文本傳輸協(xié)議),屬于TCP/IP協(xié)議棧中應(yīng)用層的協(xié)議。用于規(guī)范傳輸數(shù)據(jù)的格式,是一種客戶端和服務(wù)端傳輸?shù)囊?guī)則。
WSGI協(xié)議則是Python定義的Web服務(wù)器和框架程序通信的接口規(guī)則。兩者聯(lián)系不大,強(qiáng)行說的話,Python框架程序主要處理的是HTTP請求。
(后期可以實(shí)現(xiàn)一個(gè)WSGI協(xié)議的Python框架,用于處理HTTP請求的實(shí)驗(yàn)。)
主流Web框架,異步Web框架?
主流Web框架:Django、Flask
異步Web框架:Tornado(內(nèi)置異步模塊)、Snaic(Python自帶asyncio)、FastAPI(基于Starlette庫) 、aiohttp(基于asyncio)
asyncio,aiohttp之間的聯(lián)系?(異步編程)
asyncio是一個(gè)異步IO庫,aiohttp就是基于asyncio的異步HTTP框架(支持客戶端/服務(wù)端)
代碼設(shè)計(jì)
Python提供了基本的socket模塊:
- socket模塊;提供了標(biāo)準(zhǔn)的BSD Sockets API;
- socketserver模塊:提供了服務(wù)器中心類,簡化服務(wù)器的開發(fā);
TCP Socket服務(wù)端
socket模塊:
# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM
def echo_handler(sock ,address):
print("Get Connection from address:", address)
while True:
response = sock.recv(8192)
if not response:
break
print(f"Got {response}")
sock.sendall(response)
def echo_server(address, back_log=5):
sock = socket(AF_INET, SOCK_STREAM)
sock.bind(address)
sock.listen(back_log)
while True:
sock_client, address = sock.accept()
echo_handler(sock_client, address)
if __name__ == "__main__":
echo_server(('localhost', 5000))
代碼詳解:
- 創(chuàng)建一個(gè)基于IPV4和TCP協(xié)議的Socket,這里AF_INET指的是使用IPV4協(xié)議,SOCK_STREAM指定使用面向流的TCP協(xié)議,綁定監(jiān)聽端口,設(shè)置等待連接的最大數(shù)量
- 創(chuàng)建一個(gè)永久循環(huán),獲取客戶端請求的連接,accept()會等待并返回一個(gè)客戶端的連接;
- 連接建立后,等待客戶端數(shù)據(jù),接受完客戶端數(shù)據(jù),然后返回?cái)?shù)據(jù)給客戶端,最后關(guān)閉連接
存在的問題:當(dāng)出現(xiàn)多個(gè)客戶端請求時(shí),由于是單個(gè)線程會發(fā)生阻塞的情況,所以如果需要多線程處理多個(gè)客戶端請求,可以這樣改;
from threading import Thread
while True:
client_sock, address = sock.accept()
thread = Thread(target=echo_handler, args=(client_sock, address))
thread.start()
這樣的話,就會在每個(gè)客戶端請求的時(shí)候,生成一個(gè)子線程然后處理請求;
(但是存在一個(gè)問題:當(dāng)突然大量請求連接,消耗系統(tǒng)資源達(dá)到上限后,很可能造成程序無法處理后續(xù)請求。)
socketserver模塊:
from socketserver import BaseRequestHandler, TCPServer
class EchoHandler(BaseRequestHandler):
def handle(self):
print("Got Connection From: %s" % str(self.client_address))
while True:
msg = self.request.recv(8192)
if not msg:
break
self.request.send(msg)
if __name__ == "__main__":
server = TCPServer(("", 5000), EchoHandler)
server.serve_forever()
from socketserver import StreamRequestHandler, TCPServer, ThreadingTCPServer
import time
class EchoHandler(StreamRequestHandler):
def handle(self):
print("Got Connection Address: %s" % str(self.client_address))
for line in self.rfile:
print(line)
self.wfile.write(bytes("hello {}".format(line.decode('utf-8')).encode('utf-8')))
if __name__ == "__main__":
serv = ThreadingTCPServer(("", 5000), EchoHandler)
serv.serve_forever()
代碼詳解:
- 處理多個(gè)客戶端,初始化一個(gè)ThreadingTCPServer實(shí)例;
- 設(shè)置綁定的IP地址和端口,以及處理類;
- 使用StreamRequestHandler(使用流的請求處理程序類,類似file-like對象,提供標(biāo)準(zhǔn)文件接口簡化通信過程),重寫里面的handle方法,獲取請求數(shù)據(jù),返回?cái)?shù)據(jù)給客戶端;
TCP Socket客戶端
socket模塊:
# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM
import time
def request_handler():
start_time = time.time()
sock_client = socket(AF_INET, SOCK_STREAM)
sock_client.connect(('localhost', 5000))
book_content = ""
with open("send_books.txt", "r") as f:
book_content = f.read()
content_list = book_content.split("\n")
for content in content_list:
if content:
sock_client.send((content).encode())
time.sleep(2)
response = sock_client.recv(8192)
print(response)
end_time = time.time()
print("總共耗時(shí):", end_time-start_time)
if __name__ == "__main__":
request_handler()
UDP Socket
Socket模塊:
from socket import socket, AF_INET, SOCK_DGRAM
import time
def time_server(address):
sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(address)
while True:
msg, addr = sock.recvfrom(8192)
print('Get message from', addr)
resp = time.ctime()
sock.sendto(resp.encode('ascii'), addr)
if __name__ == "__main__":
time_server(('', 5000))
代碼不詳解,和之前的差不多,注意不同的協(xié)議就完事了
客戶端測試:
from socket import socket, AF_INET, SOCK_DGRAM
if __name__ == "__main__":
s = socket(AF_INET, SOCK_DGRAM)
s.sendto(b'hello', ('localhost', 5000))
text = s.recvfrom(8192)
print(text)
socketserver模塊:
from socketserver import BaseRequestHandler, UDPServer
import time
class TimeHandler(BaseRequestHandler):
def handle(self):
print("Got Connection %s".format(str(self.client_address)))
data = self.request[0]
print(data)
msg, sock = self.request
print(msg)
data = time.ctime()
sock.sendto(data.encode('ascii'), self.client_address)
if __name__ == "__main__":
u = UDPServer(("localhost", 9999), TimeHandler)
u.serve_forever()
代碼不在贅述,如果需要多線程處理并發(fā)操作可以使用ThreadingUDPServer
總結(jié)
關(guān)于本篇介紹Python Socket編程,大都是皮毛,只是談到了Python實(shí)際處理socket的幾個(gè)模塊,
關(guān)于socket底層方面的知識并未提及,先了解個(gè)大概,從實(shí)際使用方面出發(fā),在實(shí)際使用過程中結(jié)合
計(jì)算機(jī)網(wǎng)絡(luò)知識,能夠?qū)ocket在整個(gè)TCP/IP協(xié)議棧中的作用。
socket和socketserver模塊都可以用來編寫網(wǎng)絡(luò)程序,不同的是socketserver省事很多,你可以專注
業(yè)務(wù)邏輯,不用去理會socket的各種細(xì)節(jié),包括不限于多線程/多進(jìn)程,接收數(shù)據(jù),發(fā)送數(shù)據(jù),通信過程。
以上就是Python Socket編程詳解的詳細(xì)內(nèi)容,更多關(guān)于Python Socket編程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python 跨.py文件調(diào)用自定義函數(shù)說明
這篇文章主要介紹了Python 跨.py文件調(diào)用自定義函數(shù)說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06
python的schedule定時(shí)任務(wù)模塊二次封裝方法
今天小編就為大家分享一篇python的schedule定時(shí)任務(wù)模塊二次封裝方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-02-02
Python調(diào)用C++,通過Pybind11制作Python接口
今天小編就為大家分享一篇關(guān)于Python調(diào)用C++,通過Pybind11制作Python接口,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10
異步任務(wù)隊(duì)列Celery在Django中的使用方法
對于網(wǎng)站來說,給用戶一個(gè)較好的體驗(yàn)是很重要的事情,其中最重要的指標(biāo)就是網(wǎng)站的瀏覽速度。因此服務(wù)端要從各個(gè)方面對網(wǎng)站性能進(jìn)行優(yōu)化,這篇文章主要介紹了異步任務(wù)隊(duì)列Celery在Django中的使用方法,感興趣的小伙伴們可以參考一下2018-06-06
基于SpringBoot構(gòu)造器注入循環(huán)依賴及解決方式
這篇文章主要介紹了基于SpringBoot構(gòu)造器注入循環(huán)依賴及解決方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04
10 行Python 代碼實(shí)現(xiàn) AI 目標(biāo)檢測技術(shù)【推薦】
這篇文章主要介紹了10 行Python 代碼,實(shí)現(xiàn) AI 目標(biāo)檢測技術(shù),看完了代碼,我們在一起聊聊目標(biāo)檢測背后的技術(shù)背景,并解讀這10行Python代碼的由來和實(shí)現(xiàn)原理。感興趣的朋友跟隨小編一起看看吧2019-06-06
Python實(shí)戰(zhàn)整活之聊天機(jī)器人
這篇文章主要介紹了Python實(shí)戰(zhàn)整活之聊天機(jī)器人,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)python的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04

