Python學習之網路程式設計

stonezhu發表於2019-03-01

Python學習目錄

  1. 在Mac下使用Python3
  2. Python學習之資料型別
  3. Python學習之函式
  4. Python學習之高階特性
  5. Python學習之函數語言程式設計
  6. Python學習之模組
  7. Python學習之物件導向程式設計
  8. Python學習之物件導向高階程式設計
  9. Python學習之錯誤除錯和測試
  10. Python學習之IO程式設計
  11. Python學習之程式和執行緒
  12. Python學習之正則
  13. Python學習之常用模組
  14. 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_INET6SOCK_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()方法。

相關文章