Python3(14) Python 網(wǎng)絡(luò)編程

本系列主要學(xué)習(xí)Python的基本使用和語法知識,后續(xù)可能會圍繞著AI學(xué)習(xí)展開。
Python3 (1) Python語言的簡介
Python3 (2) Python語法基礎(chǔ)
Python3 (3) Python函數(shù)
Python3 (4) Python高級特性
Python3 (5) Python 函數(shù)式編程
Python3 (6) Python 模塊
Python3 (7) Python 面向?qū)ο缶幊?/a>
Python3 (8) Python 面向?qū)ο蟾呒壘幊?/a>
Python3 (9) Python 錯誤、調(diào)試和測試
Python3 (10) Python IO編程
Python3 (11) Python 進(jìn)程和線程
Python3 (12) Python 常用內(nèi)建模塊
Python3 (13) Python 常用第三方模塊
Python3 (14) Python 網(wǎng)絡(luò)編程

首先說一下最近要忙別的事情,Python 學(xué)習(xí)可能會中斷一小段時間,不過基礎(chǔ)篇已經(jīng)差不多學(xué)習(xí)完了,再次開始學(xué)習(xí)的時候主要以項目的形式學(xué)習(xí),穿插剩余的知識點,Python (13) 常見的第三方模塊 還沒完成,之后會寫完發(fā)出,今天主要是學(xué)習(xí) Python 的網(wǎng)絡(luò)編程,順便復(fù)習(xí)一下網(wǎng)絡(luò)編程這塊的知識。

網(wǎng)絡(luò)編程

網(wǎng)絡(luò)編程就是如何在程序中實現(xiàn)兩臺計算機(jī)的通信,現(xiàn)在的應(yīng)用基本上都需要網(wǎng)絡(luò),單機(jī)應(yīng)用基本上很少,所以網(wǎng)絡(luò)編程是一個非常重要又非?;A(chǔ)的知識點,并且網(wǎng)絡(luò)編程對所有開發(fā)語言都是一樣的,Python也不例外。用Python進(jìn)行網(wǎng)絡(luò)編程,就是在Python程序本身這個進(jìn)程內(nèi),連接別的服務(wù)器進(jìn)程的通信端口進(jìn)行通信。

TCP/IP簡介

從互聯(lián)網(wǎng)說起,之所以現(xiàn)在互聯(lián)網(wǎng)無處不在,就是因為計算機(jī)網(wǎng)絡(luò)在發(fā)展過程中形成了一套全球通用的協(xié)議,互聯(lián)網(wǎng)協(xié)議簇(Internet Protocol Suite)就是通用協(xié)議標(biāo)準(zhǔn)。因為互聯(lián)網(wǎng)協(xié)議包含了上百種協(xié)議標(biāo)準(zhǔn),但是最重要的兩個協(xié)議是TCP和IP協(xié)議,所以,大家把互聯(lián)網(wǎng)的協(xié)議簡稱TCP/IP協(xié)議。

  • IP地址:通信的時候,雙方必須知道對方的標(biāo)識,互聯(lián)網(wǎng)上每個計算機(jī)的唯一標(biāo)識就是IP地址,類似123.123.123.123。如果是路由器,它就會有兩個或多個IP地址,所以,IP地址對應(yīng)的實際上是計算機(jī)的網(wǎng)絡(luò)接口,通常是網(wǎng)卡,IP 協(xié)議位于 TCP/IP 協(xié)議的第三層——網(wǎng)絡(luò)層。與傳輸層協(xié)議相比,網(wǎng)絡(luò)層的責(zé)任是提供點到點(hop by hop)的服務(wù),而傳輸層(TCP/UDP)則提供端到端(end to end)的服務(wù)。。
  • IP協(xié)議:負(fù)責(zé)把數(shù)據(jù)從一臺計算機(jī)通過網(wǎng)絡(luò)發(fā)送到另一臺計算機(jī)。數(shù)據(jù)被分割成一小塊一小塊,然后通過IP包發(fā)送出去。由于互聯(lián)網(wǎng)鏈路復(fù)雜,兩臺計算機(jī)之間經(jīng)常有多條線路,因此,路由器就負(fù)責(zé)決定如何把一個IP包轉(zhuǎn)發(fā)出去。IP包的特點是按塊發(fā)送,途徑多個路由,但不保證能到達(dá),也不保證順序到達(dá)。
  • TCP協(xié)議:TCP協(xié)議則是建立在IP協(xié)議之上的。TCP協(xié)議負(fù)責(zé)在兩臺計算機(jī)之間建立可靠連接,保證數(shù)據(jù)包按順序到達(dá)。TCP協(xié)議會通過握手建立連接,然后,對每個IP包編號,確保對方按順序收到,如果包丟掉了,就自動重發(fā)。比如用于瀏覽器的HTTP協(xié)議、發(fā)送郵件的SMTP協(xié)議都是在TCP 協(xié)議基礎(chǔ)上定義的更高級的協(xié)議。一個TCP報文除了包含要傳輸?shù)臄?shù)據(jù)外,還包含源IP地址和目標(biāo)IP地址,源端口和目標(biāo)端口。
  • Socket:Socket 是對 TCP/IP 協(xié)議族的一種封裝,是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層。從設(shè)計模式的角度看來,Socket其實就是一個門面模式,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議;Socket 還可以認(rèn)為是一種網(wǎng)絡(luò)間不同計算機(jī)上的進(jìn)程通信的一種方法,利用三元組(ip地址,協(xié)議,端口)就可以唯一標(biāo)識網(wǎng)絡(luò)中的進(jìn)程,網(wǎng)絡(luò)中的進(jìn)程通信可以利用這個標(biāo)志與其它進(jìn)程進(jìn)行交互;Socket 起源于 Unix ,Unix/Linux 基本哲學(xué)之一就是“一切皆文件”,都可以用“打開(open) –> 讀寫(write/read) –> 關(guān)閉(close)”模式來進(jìn)行操作。因此 Socket 也被處理為一種特殊的文件。

TCP編程

Socket表示“打開了一個網(wǎng)絡(luò)鏈接”,而打開一個Socket需要知道目標(biāo)計算機(jī)的IP地址和端口號,再指定協(xié)議類型即可。 創(chuàng)建TCP連接時,主動發(fā)起連接的叫客戶端,被動響應(yīng)連接的叫服務(wù)器。

  • 客戶端:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 導(dǎo)入socket庫:
import socket

# 創(chuàng)建一個socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('www.sina.com.cn', 80))
# 發(fā)送數(shù)據(jù):
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
# 接收數(shù)據(jù):
buffer = []
while True:
    # 每次最多接收1k字節(jié):
    d = s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
data = b''.join(buffer)
# 關(guān)閉連接:
s.close()
# 分離HTTP頭和網(wǎng)頁
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
# 把接收的數(shù)據(jù)寫入文件:
with open('sina.html', 'wb') as f:
    f.write(html)

輸出結(jié)果:
在目錄下生成 sina.html文件,打開就是新浪首頁。每一步操作都備注了,可以看出客戶端要發(fā)起一個網(wǎng)絡(luò)請求需要 5 步,創(chuàng)建 socket 時需要指定是那種協(xié)議,SOCK_STREAM指定使用面向流的TCP協(xié)議。接受完數(shù)據(jù)后要記得關(guān)閉 socket。

  • 服務(wù)端:
#server
# -*- coding: utf-8 -*-
import socket, threading, time
#TCP socket based on ipv4
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#listening on port
s.bind(('127.0.0.1', 9999))
s.listen(5)
print("Waiting for connection...")

def tcplink(sock, addr):
    print('Accept new connection from %s:%s...' % addr)
    sock.send(b'Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if not data or data.decode('utf-8') == 'exit':
            break
        sock.send(('Hello, %s!' %data.decode('utf-8')).encode('utf-8'))
    sock.close()
    print('Connection from %s:%s closed.' % addr)

while True:
    # receive a new connect
    sock, addr = s.accept()
    # make a new thread dispose TCPlink
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()
#client
# -*- coding:utf-8 -*-
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#establish connection
s.connect(('127.0.0.1', 9999))
#receive a welcome message
print(s.recv(1024).decode('utf-8'))
for data in [b'Lambda', b'Bond', b'alpha']:
    #send data
    s.send(data)
    print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

輸出結(jié)果:

#server:
Waiting for connection...
Accept new connection from 127.0.0.1:64705...
Connection from 127.0.0.1:64705 closed.
#client:
Welcome!
Hello, Lambda!
Hello, Bond!
Hello, alpha!

以上完成了一個客戶端使用TCP協(xié)議發(fā)出請求,服務(wù)器接收處理,返回客戶端信息的完整過程。以下是TCP編程的特點:

  • TCP 提供一種面向連接的、可靠的字節(jié)流服務(wù)
  • 在一個 TCP 連接中,僅有兩方進(jìn)行彼此通信。廣播和多播不能用于 TCP
  • TCP 使用校驗和,確認(rèn)和重傳機(jī)制來保證可靠傳輸
  • TCP 給數(shù)據(jù)分節(jié)進(jìn)行排序,并使用累積確認(rèn)保證數(shù)據(jù)的順序不變和非重復(fù)
  • TCP 使用滑動窗口機(jī)制來實現(xiàn)流量控制,通過動態(tài)改變窗口的大小進(jìn)行擁塞控制
    注意:TCP 并不能保證數(shù)據(jù)一定會被對方接收到,因為這是不可能的。TCP 能夠做到的是,如果有可能,就把數(shù)據(jù)遞送到接收方,否則就(通過放棄重傳并且中斷連接這一手段)通知用戶。因此準(zhǔn)確說 TCP 也不是 100% 可靠的協(xié)議,它所能提供的是數(shù)據(jù)的可靠遞送或故障的可靠通知。

UDP編程

TCP是建立可靠連接,并且通信雙方都可以以流的形式發(fā)送數(shù)據(jù)。相對TCP,UDP則是面向無連接的協(xié)議。使用UDP協(xié)議時,不需要建立連接,只需要知道對方的IP地址和端口號,就可以直接發(fā)數(shù)據(jù)包。但是,能不能到達(dá)就不知道了。

  • 缺點:UDP傳輸數(shù)據(jù)不可靠
  • 優(yōu)點:和TCP比,速度快,對于不要求可靠到達(dá)的數(shù)據(jù),就可以使用UDP協(xié)議

直接上代碼:

#server
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 綁定端口:
s.bind(('127.0.0.1', 9999))

print('Bind UDP on 9999...')

while True:
    # 接收數(shù)據(jù):
    data, addr = s.recvfrom(1024)
    print('Received from %s:%s.' % addr)
    reply = 'Hello, %s!' % data.decode('utf-8')
    s.sendto(reply.encode('utf-8'), addr)
#Client
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

for data in [b'Michael', b'Tracy', b'Sarah']:
    # 發(fā)送數(shù)據(jù):
    s.sendto(data, ('127.0.0.1', 9999))
    # 接收數(shù)據(jù):
    print(s.recv(1024).decode('utf-8'))

s.close()

輸出結(jié)果:

#server:
Bind UDP on 9999...
Received from 127.0.0.1:56838.
Received from 127.0.0.1:56838.
Received from 127.0.0.1:56838.
#client:
Hello, Michael!
Hello, Tracy!
Hello, Sarah!

以上就是UDP協(xié)議的使用,UDP的使用與TCP類似,但是不需要建立連接。此外,服務(wù)器綁定UDP端口和TCP端口互不沖突,也就是說,UDP的9999端口與TCP的9999端口可以各自綁定。以下是幾個UDP編程的特點:

  • UDP 缺乏可靠性。UDP 本身不提供確認(rèn),序列號,超時重傳等機(jī)制。UDP 數(shù)據(jù)報可能在網(wǎng)絡(luò)中被復(fù)制,被重新排序。即 UDP 不保證數(shù)據(jù)報會到達(dá)其最終目的地,也不保證各個數(shù)據(jù)報的先后順序,也不保證每個數(shù)據(jù)報只到達(dá)一次
  • UDP 數(shù)據(jù)報是有長度的。每個 UDP 數(shù)據(jù)報都有長度,如果一個數(shù)據(jù)報正確地到達(dá)目的地,那么該數(shù)據(jù)報的長度將隨數(shù)據(jù)一起傳遞給接收方。而 TCP 是一個字節(jié)流協(xié)議,沒有任何(協(xié)議上的)記錄邊界。
  • UDP 是無連接的。UDP 客戶和服務(wù)器之前不必存在長期的關(guān)系。UDP 發(fā)送數(shù)據(jù)報之前也不需要經(jīng)過握手創(chuàng)建連接的過程。
  • UDP 支持多播和廣播。

TCP三次握手與四次揮手機(jī)制

所謂三次握手(Three-way Handshake),是指建立一個 TCP 連接時,需要客戶端和服務(wù)器總共發(fā)送3個包。

三次握手的目的是連接服務(wù)器指定端口,建立 TCP 連接,并同步連接雙方的序列號和確認(rèn)號,交換 TCP 窗口大小信息。在 socket 編程中,客戶端執(zhí)行 connect() 時。將觸發(fā)三次握手。

第一次握手(SYN=1, seq=x):

客戶端發(fā)送一個 TCP 的 SYN 標(biāo)志位置1的包,指明客戶端打算連接的服務(wù)器的端口,以及初始序號 X,保存在包頭的序列號(Sequence Number)字段里。

發(fā)送完畢后,客戶端進(jìn)入 SYN_SEND 狀態(tài)。

第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):

服務(wù)器發(fā)回確認(rèn)包(ACK)應(yīng)答。即 SYN 標(biāo)志位和 ACK 標(biāo)志位均為1。服務(wù)器端選擇自己 ISN 序列號,放到 Seq 域里,同時將確認(rèn)序號(Acknowledgement Number)設(shè)置為客戶的 ISN 加1,即X+1。 發(fā)送完畢后,服務(wù)器端進(jìn)入 SYN_RCVD 狀態(tài)。

第三次握手(ACK=1,ACKnum=y+1)

客戶端再次發(fā)送確認(rèn)包(ACK),SYN 標(biāo)志位為0,ACK 標(biāo)志位為1,并且把服務(wù)器發(fā)來 ACK 的序號字段+1,放在確定字段中發(fā)送給對方,并且在數(shù)據(jù)段放寫ISN的+1

發(fā)送完畢后,客戶端進(jìn)入 ESTABLISHED 狀態(tài),當(dāng)服務(wù)器端接收到這個包時,也進(jìn)入 ESTABLISHED 狀態(tài),TCP 握手結(jié)束。

三次握手的過程的示意圖如下:


三次握手

TCP 的連接的拆除需要發(fā)送四個包,因此稱為四次揮手(Four-way handshake),也叫做改進(jìn)的三次握手??蛻舳嘶蚍?wù)器均可主動發(fā)起揮手動作,在 socket 編程中,任何一方執(zhí)行 close() 操作即可產(chǎn)生揮手操作。

第一次揮手(FIN=1,seq=x)

假設(shè)客戶端想要關(guān)閉連接,客戶端發(fā)送一個 FIN 標(biāo)志位置為1的包,表示自己已經(jīng)沒有數(shù)據(jù)可以發(fā)送了,但是仍然可以接受數(shù)據(jù)。

發(fā)送完畢后,客戶端進(jìn)入 FIN_WAIT_1 狀態(tài)。

第二次揮手(ACK=1,ACKnum=x+1)

服務(wù)器端確認(rèn)客戶端的 FIN 包,發(fā)送一個確認(rèn)包,表明自己接受到了客戶端關(guān)閉連接的請求,但還沒有準(zhǔn)備好關(guān)閉連接。

發(fā)送完畢后,服務(wù)器端進(jìn)入 CLOSE_WAIT 狀態(tài),客戶端接收到這個確認(rèn)包之后,進(jìn)入 FIN_WAIT_2 狀態(tài),等待服務(wù)器端關(guān)閉連接。

第三次揮手(FIN=1,seq=y)

服務(wù)器端準(zhǔn)備好關(guān)閉連接時,向客戶端發(fā)送結(jié)束連接請求,F(xiàn)IN 置為1。

發(fā)送完畢后,服務(wù)器端進(jìn)入 LAST_ACK 狀態(tài),等待來自客戶端的最后一個ACK。

第四次揮手(ACK=1,ACKnum=y+1)

客戶端接收到來自服務(wù)器端的關(guān)閉請求,發(fā)送一個確認(rèn)包,并進(jìn)入 TIME_WAIT狀態(tài),等待可能出現(xiàn)的要求重傳的 ACK 包。

服務(wù)器端接收到這個確認(rèn)包之后,關(guān)閉連接,進(jìn)入 CLOSED 狀態(tài)。

客戶端等待了某個固定時間(兩個最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,沒有收到服務(wù)器端的 ACK ,認(rèn)為服務(wù)器端已經(jīng)正常關(guān)閉連接,于是自己也關(guān)閉連接,進(jìn)入 CLOSED 狀態(tài)。

四次揮手的示意圖如下:


四次揮手

搞清楚握手和揮手規(guī)則,我們就可以設(shè)計如何防止或者說如何減少 SYN 攻擊:

  • 縮短超時(SYN Timeout)時間(TCP KeepAlive)
  • 增加最大半連接數(shù)
  • 過濾網(wǎng)關(guān)防護(hù)
  • SYN cookies技術(shù)

參考

http://m.itdecent.cn/p/9968b16b607e(圖片來源)
https://hit-alibaba.github.io/interview/basic/network/HTTP.html
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014320037274136d31bd9979d648cd822375394e29a871000

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.這篇文章不是本人原創(chuàng)的,只是個人為了對這部分知識做一個整理和系統(tǒng)的輸出而編輯成的,在此鄭重地向本文所引用文章的...
    SOMCENT閱讀 13,389評論 6 174
  • 個人認(rèn)為,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,214評論 0 8
  • 網(wǎng)絡(luò)編程 網(wǎng)絡(luò)編程對于很多的初學(xué)者來說,都是很向往的一種編程技能,但是很多的初學(xué)者卻因為很長一段時間無法進(jìn)入網(wǎng)絡(luò)編...
    程序員歐陽閱讀 2,113評論 1 37
  • 運(yùn)輸層協(xié)議概述 從通信和信息處理的角度看,運(yùn)輸層向它上面的應(yīng)用層提供通信服務(wù),它屬于面向通信部分的最高層,同時也是...
    srtianxia閱讀 2,781評論 0 2
  • 計算機(jī)網(wǎng)絡(luò)概述 網(wǎng)絡(luò)編程的實質(zhì)就是兩個(或多個)設(shè)備(例如計算機(jī))之間的數(shù)據(jù)傳輸。 按照計算機(jī)網(wǎng)絡(luò)的定義,通過一定...
    蛋炒飯_By閱讀 1,375評論 0 10

友情鏈接更多精彩內(nèi)容