Python基礎知識(32):網路程式設計(Ⅰ)
網路通訊是兩臺計算機上的兩個程式之間的通訊,而網路程式設計就是如何在程式中實現兩臺計算機的通訊
P協議負責把資料從一臺計算機通過網路傳送到另一臺計算機
TCP協議則是建立在IP協議之上的。TCP協議負責在兩臺計算機之間建立可靠連線,保證資料包按順序到達
許多常用的更高階的協議都是建立在TCP協議基礎上的,比如用於瀏覽器的HTTP協議、傳送郵件的SMTP協議等
TCP程式設計
Socket是網路程式設計的一個抽象概念。通常我們用一個Socket表示“開啟了一個網路連結”,而開啟一個Socket需要知道目標計算機的IP地址和埠號,再指定協議型別即可。
一、客戶端
大多數連線都是可靠的TCP連線。建立TCP連線時,主動發起連線的叫客戶端,被動響應連線的叫伺服器
1、建立一個基於TCP連線的Socket
#匯入socket import socket #建立一個socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#建立連線,注意引數是一個tuple
,包含地址和埠號 s.connect((`www.sina.com.cn`, 80))
TCP連線建立的是雙向通道,雙方都可以同時給對方發資料。誰先發誰後發,怎麼協調,要根據具體的協議來決定
HTTP協議規定客戶端必須先發請求給伺服器,伺服器收到後才發資料給客戶端
傳送的文字格式必須符合HTTP標準
2、建立TCP連線後,就可以向傳送請求,要求返回首頁的內容
#傳送資料 s.send(b`GET / HTTP/1.1 Host: www.sina.com.cn Connection: close `)
3、接收伺服器返回的資料
#接收資料 buffer = [] while True: #每次最多接收1k位元組 d = s.recv(1024) if d: buffer.append(d) else: break data = b``.join(buffer)
接收資料時,呼叫recv(max)
方法,一次最多接收指定的位元組數,因此,在一個while迴圈中反覆接收,直到recv()
返回空資料,表示接收完畢,退出迴圈
4、呼叫close()
方法關閉Socket,結束網路通訊
#關閉連線 s.close()
儲存網頁內容到檔案
#把網頁內容儲存到sina.html檔案 header, html = data.split(b` `, 1) print(header.decode(`utf-8`)) #把接收的資料寫入檔案 with open(`sina.html`, `wb`) as f: f.write(html)
接收到的資料包括HTTP頭和網頁本身,只需要把HTTP頭和網頁分離一下,把HTTP頭列印出來,網頁內容儲存到檔案
只需要在瀏覽器中開啟這個sina.html
檔案,就可以看到新浪的首頁了
HTTP/1.1 302 Moved Temporarily Server: nginx Date: Fri, 14 Dec 2018 08:07:21 GMT Content-Type: text/html Content-Length: 154 Connection: close Location: https://www.sina.com.cn/ X-Via-CDN: f=edge,s=ctc.xiamen.ha2ts4.35.nb.sinaedge.com,c=125.91.244.232; X-Via-Edge: 1544774841629e8f45b7d3cd64cde1173d1de
二、伺服器
伺服器程式首先要繫結一個埠並監聽來自其他客戶端的連線。如果某個客戶端連線過來了,伺服器就與該客戶端建立Socket連線,隨後的通訊就靠這個Socket連線了
一個Socket依賴4項:伺服器地址、伺服器埠、客戶端地址、客戶端埠來唯一確定一個Socket
伺服器需要同時響應多個客戶端的請求,每個連線都需要一個新的程式或者新的執行緒來處理,否則,伺服器一次就只能服務一個客戶端
1、建立一個基於IPv4和TCP協議的Socket
#建立一個基於IPv4和TCP協議的Socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2、繫結監聽的地址和埠
#監聽埠 s.bind((`127.0.0.1`, 9999))
伺服器可能有多塊網路卡,可以繫結到某一塊網路卡的IP地址上,也可以用0.0.0.0
繫結到所有的網路地址,還可以用127.0.0.1
繫結到本機地址
127.0.0.1
是一個特殊的IP地址,表示本機地址,如果繫結到這個地址,客戶端必須同時在本機執行才能連線,也就是說,外部的計算機無法連線進來
埠號需要預先指定。因為我們寫的這個服務不是標準服務,所以用9999
這個埠號
注意:小於1024
的埠號必須要有管理員許可權才能繫結
3、呼叫listen()
方法監聽埠
傳入的引數指定等待連線的最大數量
s.listen(5) print(`Waiting for connection...`)
4、通過一個永久迴圈接受來自客戶端的連線
while True: #接受一個新連線 sock, addr = s.accept() #建立新執行緒來處理TCP連線 t = threading.Thread(target=tcplink, args=(sock, addr)) t.start()
accept()
會等待並返回一個客戶端的連線
5、建立新程式處理連線
每個連線都必須建立新執行緒(或程式)來處理,否則,單執行緒在處理連線的過程中,無法接受其他客戶端的連線
#建立新程式處理連線 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 close.` % addr)
連線建立後,伺服器首先發一條歡迎訊息,然後等待客戶端資料,並加上Hello
再傳送給客戶端
如果客戶端傳送了exit
字串,就直接關閉連線
6、編寫一個客戶端程式測試這個伺服器程式
#tcp_client.py import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #建立連線 s.connect((`127.0.0.1`, 9999)) #接收歡迎訊息 print(s.recv(1024).decode(`utf-8`)) for data in [b`Alice`, b`Bob`, b`Jack`]: s.send(data) print(s.recv(1024).decode(`utf-8`)) s.send(b`exit`) s.close()
分別執行tcp_client.py和tcp_server.py
tcp_server.py的結果: Waiting for connection... Accept new connection from 127.0.0.1:60647... Connection from 127.0.0.1:60647 close. tcp_client.py的結果: Welcome Hello, Alice! Hello, Bob! Hello, Jack!
參考資料:
1、廖雪峰學習官網
2、寧致樂水的博文:https://blog.csdn.net/qq_31603575/article/details/80089707