本文介紹使用Python進行Socket網路程式設計,假設讀者已經具備了基本的網路程式設計知識和Python的基本語法知識,本文中的程式碼如果沒有說明則都是執行在Python 3.4下。
Python的socket功能封裝在socket庫中,要使用socket,記得先import socket
,socket庫的詳細介紹參見官方文件。
建立Socket
首先建立一個socket,使用socket庫中得socket函式建立。
1 2 3 4 5 |
import socket # create an INET, STREAM socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
例子中建立了一個TCP socket,socket.socket
函式的前兩個引數的預設值是socket.AF_INET
和socket.SOCK_STREAM
,建立TCP socket時可以直接寫成socket.socket()
。
連線伺服器
使用socket的connect
函式連線到伺服器,以下幾種引數都是合法的。
1 2 3 4 |
s.connect(('localhost', 8000)) s.connect(('127.0.0.1', 8000)) s.connect(('www.baidu.com', 80)) |
傳送資料
傳送資料有兩個方法send和sendall,send
不能保證所有的資料都傳送完了,它會返回已傳送資料的長度,程式要迴圈傳送資料直到所有資料都已傳送完畢。
1 2 3 4 5 6 7 8 |
def mysend(s, msg): total_len = len(msg) total_sent = 0 while total_sent < total_len: sent = s.send(msg[total_sent:]) if sent == 0: raise RuntimeError("socket connection broken") total_sent += sent |
sendall
能夠保證所有的資料都已傳送完畢,除非傳送過程中出現了錯誤,它實際上也是迴圈傳送資料直到所有資料傳送完成。
這裡還要講一個需要特別注意的地方,從一個例子開始吧:
1 2 3 4 5 |
import socket s = socket.socket() s.connect(('www.baidu.com', 80)) s.sendall('test') |
都是上面講過的東西,沒什麼特別的,分別在Python 2和Python 3中執行以上的程式碼,結果是:
1 2 3 4 5 6 |
# Python 2.7 >>> import socket >>> s = socket.socket() >>> s.connect(('www.baidu.com', 80)) >>> s.sendall('test') |
Python 2中執行成功。
1 2 3 4 5 6 7 8 |
# Python 3.4 >>> import socket >>> s = socket.socket() >>> s.connect(('www.baidu.com', 80)) >>> s.sendall('test') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' does not support the buffer interface |
Python 3中卻發生了異常。
同樣的程式碼換個環境卻不能執行了,我沒有寫錯呀,怒砸電腦。好吧,你確實沒寫錯,是環境變了,導致這個結果的變化請移步官方的說明。
接收資料
使用recv
函式接收資料:
1 2 |
data = s.recv(4096) |
在Python 3中返回的是bytes物件,在Python 2中返回的是string。注意函式返回的資料長度是小於或者等於引數指定的長度的,要接收到指定長度的資料,需要迴圈接收資料。
1 2 3 4 5 6 7 8 9 10 |
def myreceive(s, msglen): chunks = [] bytes_recd = 0 while bytes_recd < msglen: chunk = s.recv(min(msglen - bytes_recd, 2048)) if chunk == b'': raise RuntimeError("socket connection broken") chunks.append(chunk) bytes_recd = bytes_recd + len(chunk) return b''.join(chunks) |
關閉連線
當連線不再需要時可以使用close
關閉socket連線,關閉後的連線不能再進行任何操作。當一個socket被回收時會自動關閉,但是不要依賴這種機制,不需要socket時就主動的close。
服務端
服務端程式執行的步驟:
1. 建立服務端socket
1. 將服務端socket繫結到指定的地址和埠
1. 監聽連線
1. 接受客戶端連線
1. 處理客戶端的資料
1. 關閉客戶端連線
一個簡單的echo server示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import socket HOST = '' PORT = 10022 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(10) conn, addr = s.accept() while True: data = conn.recv(1024) if not data: break conn.sendall(data) conn.close() |
客戶端程式:
1 2 3 4 5 6 7 8 9 10 11 12 |
import socket HOST = 'localhost' PORT = 10022 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.sendall(b'hello socket') data = s.recv(1024) print('Received', repr(data)) s.close() |
錯誤處理
socket處理過程中發生錯誤會丟擲異常,socket相關的異常有:
– socket.error
– socket.herror
– socket.gaierror
– socket.timeout
1 2 3 4 5 6 7 8 9 10 11 12 |
Python</code><code>import socket HOST = None PORT = 10022 try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(10) except: socket.error as msg: print(msg) |