1. 程式人生 > >Python Socket 程式設計示例 Echo Server

Python Socket 程式設計示例 Echo Server

簡評:我們已經從「Python Socket 程式設計概覽」瞭解了 socket API 的概述以及客戶端和伺服器的通訊方式,接下來讓我們建立第一個客戶端和伺服器,我們將從一個簡單的實現開始,伺服器將簡單地回顯它接收到客戶端的任何內容。本文將詳細解釋伺服器部分的程式碼。

下面是伺服器程式碼儲存到 echo-server.py檔案:

#!/usr/bin/env python3

import socket

HOST = '127.0.0.1'  # Standard loopback interface address (localhost)
PORT = 65432        # Port to listen on (non-privileged ports are > 1023)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)

socket.socket()建立一個支援上下文管理器型別的 socket 物件,因此可以在 with 語句中使用它,沒有必要去呼叫 s.close():

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    pass  # Use the socket object without calling s.close().

傳遞給socket()的引數指定地址族和 socket 型別。 AF_INET 指的是 IPv4 的網路地址。 SOCK_STREAM是 TCP 的 socket 型別,TCP 用於在網路中傳輸訊息。

bind()用於將 socket 與特定網路介面和埠號相關聯:

HOST = '127.0.0.1'  # Standard loopback interface address (localhost)
PORT = 65432        # Port to listen on (non-privileged ports are > 1023)

# ...

s.bind((HOST, PORT))

傳遞給bind()的值取決於 socket 的地址族。在這個例子中,我們使用的是socket.AF_INET(IPv4)所以它接受一個(host, port)元組。

host 可以是主機名、IP 地址或空字串。如果使用 IP 地址,則主機應為 IPv4 格式的地址字串,127.0.0.1 是環回介面的標準 IPv4 地址,因此只有主機上的程序才能連線到伺服器。如果傳遞空字串,則伺服器將接受所有可用 IPv4 介面上的連線。

port 應為 1-65535 之間的整數(0 被保留),它是接受來自客戶端連線的 TCP 埠號,如果埠號 <1024,某些系統可能需要超級使用者許可權。

listen()使伺服器能夠 accept()連線,這使它成為一個「listening」socket:

s.listen()
conn, addr = s.accept()

accept()阻塞並等待傳入請求連線。當客戶端連線時,它返回一個表示連線的 socket 物件和一個儲存客戶端地址的元組,該元組包含用於 IPv4 連線的(host, port)或用於 IPv6的(host, port, flowinfo, scopeid)。

必須要理解的一件事是我們現在有一個來自accept()的新 socket 物件,這很重要,因為它是用於與客戶端通訊的 socket,它與伺服器用於接受新連線的偵聽 socket 不同:

conn, addr = s.accept()
with conn:
    print('Connected by', addr)
    while True:
        data = conn.recv(1024)
        if not data:
            break
        conn.sendall(data)

從accept()獲取客戶端 socket 物件conn後,使用無限迴圈來迴圈阻塞對 conn.recv()的呼叫。這將讀取客戶端傳送的任何資料,並使用 conn.sendall()將其回送回來。

如果conn.recv()返回一個空位元組物件b'',則客戶端關閉連線並終止迴圈。 with 語句與 conn 一起使用以自動關閉塊末尾的 socket。