Python中的网络编程之TCP
Python中的網(wǎng)絡(luò)編程之TCP
文章目錄
- Python中的網(wǎng)絡(luò)編程之TCP
- 1、TCP介紹
- 2、TCP特點(diǎn)
- 3、TCP與UDP的不同點(diǎn)
- 4.tcp通信模型
- 5、tcp客戶端
- 6.tcp服務(wù)器
- 7、TCP的注意事項(xiàng)
- 8.文件下載案例
- 9. TCP的三字握手四次揮手
- 10、TCP長/短連接
- 1.tcp長連接和短連接
- 2. TCP短連接
- 3. TCP長連接
- 4.TCP長/短連接操作過程
- 5.TCP長/短連接的優(yōu)點(diǎn)和缺點(diǎn)
- 6.TCP長/短連接的應(yīng)用場景
1、TCP介紹
TCP協(xié)議,傳輸控制協(xié)議(英語:Transmission Control Protocol,縮寫為 TCP)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,由IETF的RFC 793定義。
TCP通信需要經(jīng)過創(chuàng)建連接、數(shù)據(jù)傳送、終止連接三個(gè)步驟。
TCP通信模型中,在通信開始之前,一定要先建立相關(guān)的鏈接,才能發(fā)送數(shù)據(jù),類似于生活中,“打電話”",UDP則向發(fā)送信件。
2、TCP特點(diǎn)
1. 面向連接
- 通信雙方必須先建立連接才能進(jìn)行數(shù)據(jù)的傳輸,雙方都必須為該連接分配必要的系統(tǒng)內(nèi)核資源,以管理連接的狀態(tài)和連接上的傳輸。
- 雙方間的數(shù)據(jù)傳輸都可以通過這一個(gè)連接進(jìn)行。
- 完成數(shù)據(jù)交換后,雙方必須斷開此連接,以釋放系統(tǒng)資源。
- 這種連接是一對一的,因此TCP不適用于廣播的應(yīng)用程序,基于廣播的應(yīng)用程序請使用UDP協(xié)議。
2. 可靠傳輸
1)TCP采用發(fā)送應(yīng)答機(jī)制
TCP發(fā)送的每個(gè)報(bào)文段都必須得到接收方的應(yīng)答才認(rèn)為這個(gè)TCP報(bào)文段傳輸成功
2)超時(shí)重傳
發(fā)送端發(fā)出一個(gè)報(bào)文段之后就啟動定時(shí)器,如果在定時(shí)時(shí)間內(nèi)沒有收到應(yīng)答就重新發(fā)送這個(gè)報(bào)文段。
TCP為了保證不發(fā)生丟包,就給每個(gè)包一個(gè)序號,同時(shí)序號也保證了傳送到接收端實(shí)體的包的按序接收。然后接收端實(shí)體對已成功收到的包發(fā)回一個(gè)相應(yīng)的確認(rèn)(ACK);如果發(fā)送端實(shí)體在合理的往返時(shí)延(RTT)內(nèi)未收到確認(rèn),那么對應(yīng)的數(shù)據(jù)包就被假設(shè)為已丟失將會被進(jìn)行重傳。
3)錯誤校驗(yàn)
TCP用一個(gè)校驗(yàn)和函數(shù)來檢驗(yàn)數(shù)據(jù)是否有錯誤;在發(fā)送和接收時(shí)都要計(jì)算校驗(yàn)和。
4) 流量控制和阻塞管理
流量控制用來避免主機(jī)發(fā)送得過快而使接收方來不及完全收下。
3、TCP與UDP的不同點(diǎn)
| TCP的傳輸是可靠傳輸 | UDP的傳輸是不可靠傳輸。 |
| TCP是基于連接的協(xié)議,在正式收發(fā)數(shù)據(jù)前,必須和對方建立可靠的連接。 | UDP是和TCP相對應(yīng)的協(xié)議,它是面向非連接的協(xié)議,它不與對方建立連接,而是直接把數(shù)據(jù)包發(fā)送出去 |
| TCP是一種可靠的通信服務(wù),負(fù)載相對而言比較大,TCP采用套接字(socket)或者端口(port)來建立通信。 | UDP是一種不可靠的網(wǎng)絡(luò)服務(wù),負(fù)載比較小。 |
| TCP和UDP結(jié)構(gòu)不同,TCP包括序號、確認(rèn)信號、數(shù)據(jù)偏移、控制標(biāo)志(通常說的URG、ACK、PSH、RST、SYN、FIN)、窗口、校驗(yàn)和、緊急指針、選項(xiàng)等信息。 | UDP包含長度和校驗(yàn)和信息。 |
| TCP提供超時(shí)重發(fā),丟棄重復(fù)數(shù)據(jù),檢驗(yàn)數(shù)據(jù),流量控制等功能,保證數(shù)據(jù)能從一端傳到另一端。 | UDP不提供可靠性,它只是把應(yīng)用程序傳給IP層的數(shù)據(jù)報(bào)發(fā)送出去,但是并不能保證它們能到達(dá)目的地。 |
| TCP在發(fā)送數(shù)據(jù)包前在通信雙方有一個(gè)三次握手機(jī)制,確保雙方準(zhǔn)備好,在傳輸數(shù)據(jù)包期間,TCP會根據(jù)鏈路中數(shù)據(jù)流量的大小來調(diào)節(jié)傳送的速率,傳輸時(shí)如果發(fā)現(xiàn)有丟包,會有嚴(yán)格的重傳機(jī)制,故而傳輸速度很慢。 | UDP在傳輸數(shù)據(jù)報(bào)前不用在客戶和服務(wù)器之間建立一個(gè)連接,且沒有超時(shí)重發(fā)等機(jī)制,故而傳輸速度很快。 |
| TCP支持全雙工和并發(fā)的TCP連接,提供確認(rèn)、重傳與擁塞控制。 | UDP適用于哪些系統(tǒng)對性能的要求高于數(shù)據(jù)完整性的要求,需要“簡短快捷”的數(shù)據(jù)交換、需要多播和廣播的應(yīng)用環(huán)境。 |
4.tcp通信模型
- udp通信模型中,在通信開始之前,不需要建立相關(guān)的鏈接,只需要發(fā)送數(shù)據(jù)即可,類似于生活中,“寫信"”
- tcp通信模型中,在通信開始之前,一定要先建立相關(guān)的鏈接,才能發(fā)送數(shù)據(jù),類似于生活中,“打電話”"
TCP通信模型:
5、tcp客戶端
所謂的服務(wù)器端:就是提供服務(wù)的一方,而客戶端,就是需要被服務(wù)的一方
tcp客戶端構(gòu)建流程
tcp的客戶端要比服務(wù)器端簡單很多,如果說服務(wù)器端是需要自己買手機(jī)、查手機(jī)卡、設(shè)置鈴聲、等待別人打電話流程的話,那么客戶端就只需要找一個(gè)電話亭,拿起電話撥打即可,流程要少很多
示例代碼:
from socket import *# 創(chuàng)建socket tcp_client_socket = socket(AF_INET, SOCK_STREAM)# 目的信息 server_ip = input("請輸入服務(wù)器ip:") server_port = int(input("請輸入服務(wù)器port:"))# 鏈接服務(wù)器 tcp_client_socket.connect((server_ip, server_port))# 提示用戶輸入數(shù)據(jù) send_data = input("請輸入要發(fā)送的數(shù)據(jù):")tcp_client_socket.send(send_data.encode("gbk"))# 接收對方發(fā)送過來的數(shù)據(jù),最大接收1024個(gè)字節(jié) recvData = tcp_client_socket.recv(1024) print('接收到的數(shù)據(jù)為:', recvData.decode('gbk'))# 關(guān)閉套接字 tcp_client_socket.close()6.tcp服務(wù)器
在程序中,如果想要完成一個(gè)tcp服務(wù)器的功能,需要的流程如下:
- socket創(chuàng)建一個(gè)套接字
- bind綁定ip和port
- listen使套接字變?yōu)榭梢员粍渔溄?/li>
- accept等待客戶端的鏈接
- recv/send接收發(fā)送數(shù)據(jù)
一個(gè)很簡單的tcp服務(wù)器如下:
from socket import *# 創(chuàng)建socket tcp_server_socket = socket(AF_INET, SOCK_STREAM)# 本地信息 address = ('', 7788)# 綁定 tcp_server_socket.bind(address)# 使用socket創(chuàng)建的套接字默認(rèn)的屬性是主動的,使用listen將其變?yōu)楸粍拥?#xff0c;這樣就可以接收別人的鏈接了 tcp_server_socket.listen(128)# 如果有新的客戶端來鏈接服務(wù)器,那么就產(chǎn)生一個(gè)新的套接字專門為這個(gè)客戶端服務(wù) # client_socket用來為這個(gè)客戶端服務(wù) # tcp_server_socket就可以省下來專門等待其他新客戶端的鏈接 client_socket, clientAddr = tcp_server_socket.accept()# 接收對方發(fā)送過來的數(shù)據(jù) recv_data = client_socket.recv(1024) # 接收1024個(gè)字節(jié) print('接收到的數(shù)據(jù)為:', recv_data.decode('gbk'))# 發(fā)送一些數(shù)據(jù)到客戶端 client_socket.send("thank you !".encode('gbk'))# 關(guān)閉為這個(gè)客戶端服務(wù)的套接字,只要關(guān)閉了,就意味著為不能再為這個(gè)客戶端服務(wù)了,如果還需要服務(wù),只能再次重新連接 client_socket.close()7、TCP的注意事項(xiàng)
- 服務(wù)器一般情況下都需要綁定端口號,否則客戶端找不到這個(gè)服務(wù)器
- tcp客戶端一般不綁定,因?yàn)槭侵鲃渔溄臃?wù)器,所以只要確定好服務(wù)器的ip、port等信息就好,本地客戶端可以隨機(jī)
- tcp服務(wù)器中通過listen可以將socket創(chuàng)建出來的主動套接字變?yōu)楸粍拥?#xff0c;這是做tcp服務(wù)器時(shí)必須要做的
- 當(dāng)客戶端需要鏈接服務(wù)器時(shí),就需要使用connect進(jìn)行鏈接,udp是不需要鏈接的而是直接發(fā)送,但是tcp必須先鏈接,只有鏈接成功才能通信
- 當(dāng)一個(gè)tcp客戶端連接服務(wù)器時(shí),服務(wù)器端會有1個(gè)新的套接字,這個(gè)套接字用來標(biāo)記這個(gè)客戶端,單獨(dú)為這個(gè)客戶端服務(wù)
- listen后的套接字是被動套接字,用來接收新的客戶端的鏈接請求的,而accept返回的新套接字是標(biāo)記這個(gè)新客戶端的
- 關(guān)閉listen后的套接字意味著被動套接字關(guān)閉了,會導(dǎo)致新的客戶端不能夠鏈接服務(wù)器,但是之前已經(jīng)鏈接成功的客戶端正常通信。
- 關(guān)閉accept返回的套接字意味著這個(gè)客戶端已經(jīng)服務(wù)完畢
- 當(dāng)客戶端的套接字調(diào)用close后,服務(wù)器端會recv解堵塞,并且返回的長度為0,因此服務(wù)器可以通過返回?cái)?shù)據(jù)的長度來區(qū)別客戶端是否已經(jīng)下線
8.文件下載案例
server:
from socket import * import sysdef get_file_content(file_name):"""獲取文件的內(nèi)容"""try:with open(file_name, "rb") as f:content = f.read()return contentexcept:print("沒有下載的文件:%s" % file_name)def main():if len(sys.argv) != 2:print("請按照如下方式運(yùn)行:python3 xxx.py 7890")returnelse:# 運(yùn)行方式為python3 xxx.py 7890port = int(sys.argv[1])# 創(chuàng)建sockettcp_server_socket = socket(AF_INET, SOCK_STREAM)# 本地信息address = ('', port)# 綁定本地信息tcp_server_socket.bind(address)# 將主動套接字變?yōu)楸粍犹捉幼?/span>tcp_server_socket.listen(128)while True:# 等待客戶端的鏈接,即為這個(gè)客戶端發(fā)送文件client_socket, clientAddr = tcp_server_socket.accept()# 接收對方發(fā)送過來的數(shù)據(jù)recv_data = client_socket.recv(1024) # 接收1024個(gè)字節(jié)file_name = recv_data.decode("utf-8")print("對方請求下載的文件名為:%s" % file_name)file_content = get_file_content(file_name)# 發(fā)送文件的數(shù)據(jù)給客戶端# 因?yàn)楂@取打開文件時(shí)是以rb方式打開,所以file_content中的數(shù)據(jù)已經(jīng)是二進(jìn)制的格式,因此不需要encode編碼if file_content:client_socket.send(file_content)# 關(guān)閉這個(gè)套接字client_socket.close()# 關(guān)閉監(jiān)聽套接字tcp_server_socket.close()if __name__ == "__main__":main()client:
from socket import *def main():# 創(chuàng)建sockettcp_client_socket = socket(AF_INET, SOCK_STREAM)# 目的信息server_ip = input("請輸入服務(wù)器ip:")server_port = int(input("請輸入服務(wù)器port:"))# 鏈接服務(wù)器tcp_client_socket.connect((server_ip, server_port))# 輸入需要下載的文件名file_name = input("請輸入要下載的文件名:")# 發(fā)送文件下載請求tcp_client_socket.send(file_name.encode("utf-8"))# 接收對方發(fā)送過來的數(shù)據(jù),最大接收1024個(gè)字節(jié)(1K)recv_data = tcp_client_socket.recv(1024)# print('接收到的數(shù)據(jù)為:', recv_data.decode('utf-8'))# 如果接收到數(shù)據(jù)再創(chuàng)建文件,否則不創(chuàng)建if recv_data:with open("[接收]"+file_name, "wb") as f:f.write(recv_data)# 關(guān)閉套接字tcp_client_socket.close()if __name__ == "__main__":main()9. TCP的三字握手四次揮手
Linux模塊詳解TCP
10、TCP長/短連接
1.tcp長連接和短連接
- TCP在真正的讀寫操作之前,server與client之間必須建立一個(gè)連接
- 當(dāng)讀寫操作完成后,雙方不再需要這個(gè)連接時(shí)它們可以釋放這個(gè)連接,
- 連接的建立通過三次握手,釋放則需要四次握手,
- 所以說每個(gè)連接的建立都是需要資源消耗和時(shí)間消耗的。
2. TCP短連接
模擬一種TCP短連接的情況:
在步驟5中,一般都是 client 先發(fā)起 close 操作。當(dāng)然也不排除有特殊的情況。
從上面的描述看,短連接一般只會在 client/server 間傳遞一次讀寫操作!
3. TCP長連接
再模擬一種長連接的情況:
4.TCP長/短連接操作過程
4.1 短連接的操作步驟是:
建立連接——數(shù)據(jù)傳輸——關(guān)閉連接…建立連接——數(shù)據(jù)傳輸——關(guān)閉連接
4.2 長連接的操作步驟是:
建立連接——數(shù)據(jù)傳輸…(保持連接)…數(shù)據(jù)傳輸——關(guān)閉連接
5.TCP長/短連接的優(yōu)點(diǎn)和缺點(diǎn)
- 長連接可以省去較多的TCP建立和關(guān)閉的操作,減少浪費(fèi),節(jié)約時(shí)間。
- 對于頻繁請求資源的客戶來說,較適用長連接。
- client與server之間的連接如果一直不關(guān)閉的話,會存在一個(gè)問題,
- 隨著客戶端連接越來越多,server早晚有扛不住的時(shí)候,這時(shí)候server端需要采取一些策略,
- 如關(guān)閉一些長時(shí)間沒有讀寫事件發(fā)生的連接,這樣可以避免一些惡意連接導(dǎo)致server端服務(wù)受損;
- 如果條件再允許就可以以客戶端機(jī)器為顆粒度,限制每個(gè)客戶端的最大長連接數(shù),這樣可以完全避免某個(gè)蛋疼的客戶端連累后端服務(wù)。
- 短連接對于服務(wù)器來說管理較為簡單,存在的連接都是有用的連接,不需要額外的控制手段。
- 但如果客戶請求頻繁,將在TCP的建立和關(guān)閉操作上浪費(fèi)時(shí)間和帶寬。
6.TCP長/短連接的應(yīng)用場景
- 長連接多用于操作頻繁,點(diǎn)對點(diǎn)的通訊,而且連接數(shù)不能太多情況。
- 每個(gè)TCP連接都需要三次握手,這需要時(shí)間,如果每個(gè)操作都是先連接,
- 再操作的話那么處理速度會降低很多,所以每個(gè)操作完后都不斷開,
- 再次處理時(shí)直接發(fā)送數(shù)據(jù)包就OK了,不用建立TCP連接。
- 例如:數(shù)據(jù)庫的連接用長連接,如果用短連接頻繁的通信會造成socket錯誤
- 而且頻繁的socket 創(chuàng)建也是對資源的浪費(fèi)。
- 而像WEB網(wǎng)站的http服務(wù)一般都用短鏈接,因?yàn)殚L連接對于服務(wù)端來說會耗費(fèi)一定的資源,
- 而像WEB網(wǎng)站這么頻繁的成千上萬甚至上億客戶端的連接用短連接會更省一些資源,
- 如果用長連接,而且同時(shí)有成千上萬的用戶,如果每個(gè)用戶都占用一個(gè)連接的話,
- 那可想而知吧。所以并發(fā)量大,但每個(gè)用戶無需頻繁操作情況下需用短連好。
總結(jié)
以上是生活随笔為你收集整理的Python中的网络编程之TCP的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python中的网络编程之UDP
- 下一篇: Python中的HTTP协议