Python學習目錄
- 在Mac下使用Python3
- Python學習之資料型別
- Python學習之函式
- Python學習之高階特性
- Python學習之函數語言程式設計
- Python學習之模組
- Python學習之物件導向程式設計
- Python學習之物件導向高階程式設計
- Python學習之錯誤除錯和測試
- Python學習之IO程式設計
- Python學習之程式和執行緒
- Python學習之正則
- Python學習之常用模組
- Python學習之網路程式設計
網際網路的實現,分成好幾層。每一層都有自己的功能,就像建築物一樣,每一層都靠下一層支援。如何分層有不同的模型,有的模型分七層,有的分四層。我覺得,把網際網路分成五層,比較容易解釋。最底下的一層叫做"實體層"(Physical Layer),最上面的一層叫做"應用層"(Application Layer),中間的三層(自下而上)分別是"連結層"(Link Layer)、"網路層"(Network Layer)和"傳輸層"(Transport Layer)。越下面的層,越靠近硬體;越上面的層,越靠近使用者。
TCP程式設計
Socket是網路程式設計的一個抽象概念。通常我們用一個Socket表示“開啟了一個網路連結”,而開啟一個Socket需要知道目標計算機的IP地址和埠號,再指定協議型別即可。
客戶端
建立Socket
# 匯入socket庫:
import socket
# 建立一個socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連線:
s.connect(('www.sina.com.cn', 80))
複製程式碼
建立Socket
時,AF_INET
指定使用IPv4協議,如果要用更先進的IPv6,就指定為AF_INET6
。SOCK_STREAM
指定使用面向流的TCP協議,這樣,一個Socket
物件就建立成功,但是還沒有建立連線。
連線伺服器
s.connect(('www.sina.com.cn', 80))
複製程式碼
注意引數是一個tuple
,包含地址和埠號。
傳送請求
# 傳送資料:
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
複製程式碼
TCP連線建立的是雙向通道,雙方都可以同時給對方發資料。但是誰先發誰後發,怎麼協調,要根據具體的協議來決定。例如,HTTP協議規定客戶端必須先發請求給伺服器,伺服器收到後才發資料給客戶端。
接收資料
# 接收資料:
buffer = []
while True:
# 每次最多接收1k位元組:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
複製程式碼
關閉Socket
# 接收資料:
buffer = []
while True:
# 每次最多接收1k位元組:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
複製程式碼
伺服器
建立Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
複製程式碼
建立一個基於IPv4和TCP協議的Socket。
繫結監聽的地址和埠
# 監聽埠:
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')
複製程式碼
listen()
方法傳入的引數指定等待連線的最大數量。
接受客戶端連線
while True:
# 接受一個新連線:
sock, addr = s.accept()
# 建立新執行緒來處理TCP連線:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
複製程式碼
每個連線都必須建立新執行緒(或程式)來處理,否則,單執行緒在處理連線的過程中,無法接受其他客戶端的連線:
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)
複製程式碼
UDP程式設計
TCP是建立可靠連線,並且通訊雙方都可以以流的形式傳送資料。相對TCP,UDP則是面向無連線的協議。
使用UDP協議時,不需要建立連線,只需要知道對方的IP地址和埠號,就可以直接發資料包。但是,能不能到達就不知道了。
雖然用UDP傳輸資料不可靠,但它的優點是和TCP比,速度快,對於不要求可靠到達的資料,就可以使用UDP協議。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 繫結埠:
s.bind(('127.0.0.1', 9999))
複製程式碼
建立Socket時,SOCK_DGRAM
指定了這個Socket的型別是UDP。繫結埠和TCP一樣,但是不需要呼叫listen()
方法,而是直接接收來自任何客戶端的資料:
print('Bind UDP on 9999...')
while True:
# 接收資料:
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
s.sendto(b'Hello, %s!' % data, addr)
複製程式碼
recvfrom()
方法返回資料和客戶端的地址與埠,這樣,伺服器收到資料後,直接呼叫sendto()
就可以把資料用UDP發給客戶端。
注意這裡省掉了多執行緒,因為這個例子很簡單。
客戶端使用UDP時,首先仍然建立基於UDP的Socket,然後,不需要呼叫connect()
,直接通過sendto()
給伺服器發資料:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:
# 傳送資料:
s.sendto(data, ('127.0.0.1', 9999))
# 接收資料:
print(s.recv(1024).decode('utf-8'))
s.close()
複製程式碼
從伺服器接收資料仍然呼叫recv()
方法。