pythontcp TCP程式設計

2021-10-11 11:55:44 字數 4058 閱讀 8860

socket是網路程式設計的乙個抽象概念。通常我們用乙個socket表示「開啟了乙個網路鏈結」,而開啟乙個socket需要知道目標計算機的ip位址和埠號,再指定協議型別即可。

客戶端大多數連線都是可靠的tcp連線。建立tcp連線時,主動發起連線的叫客戶端,被動響應連線的叫伺服器。

所以,我們要建立乙個基於tcp連線的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,包含位址和埠號。

# 傳送資料:

tcp連線建立的是雙向通道,雙方都可以同時給對方發資料。但是誰先發誰後發,怎麼協調,要根據具體的協議來決定。例如,http協議規定客戶端必須先發請求給伺服器,伺服器收到後才發資料給客戶端。

# 接收資料:

buffer =

while true:

# 每次最多接收1k位元組:

d = s.recv(1024)

if d:

else:

break

data = b''.join(buffer)

接收資料時,呼叫recv(max)方法,一次最多接收指定的位元組數,因此,在乙個while迴圈中反覆接收,直到recv()返回空資料,表示接收完畢,退出迴圈。

當我們接收完資料後,呼叫close()方法關閉socket,這樣,一次完整的網路通訊就結束了:

# 關閉連線:

s.close()

接收到的資料報括http頭和網頁本身,我們只需要把http頭和網頁分離一下,把http頭列印出來,網頁內容儲存到檔案:

header, html = data.split(b'\r\n\r\n', 1)

print(header.decode('utf-8'))

# 把接收的資料寫入檔案:

with open('sina.html', 'wb') as f:

f.write(html)

伺服器和客戶端程式設計相比,伺服器程式設計就要複雜一些。

伺服器程序首先要繫結乙個埠並監聽來自其他客戶端的連線。如果某個客戶端連線過來了,伺服器就與該客戶端建立socket連線,隨後的通訊就靠這個socket連線了。

所以,伺服器會開啟固定埠(比如80)監聽,每來乙個客戶端連線,就建立該socket連線。由於伺服器會有大量來自客戶端的連線,所以,伺服器要能夠區分乙個socket連線是和哪個客戶端繫結的。乙個socket依賴4項:伺服器位址、伺服器端口、客戶端位址、客戶端埠來唯一確定乙個socket。

但是伺服器還需要同時響應多個客戶端的請求,所以,每個連線都需要乙個新的程序或者新的執行緒來處理,否則,伺服器一次就只能服務乙個客戶端了。

我們來編寫乙個簡單的伺服器程式,它接收客戶端連線,把客戶端發過來的字串加上hello再發回去。

首先,建立乙個基於ipv4和tcp協議的socket:

s = socket.socket(socket.af_inet, socket.sock_stream)

然後,我們要繫結監聽的位址和埠。伺服器可能有多塊網絡卡,可以繫結到某一塊網絡卡的ip位址上,也可以用0.0.0.0繫結到所有的網路位址,還可以用127.0.0.1繫結到本機位址。127.0.0.1是乙個特殊的ip位址,表示本機位址,如果繫結到這個位址,客戶端必須同時在本機執行才能連線,也就是說,外部的計算機無法連線進來。

埠號需要預先指定。因為我們寫的這個服務不是標準服務,所以用9999這個埠號。請注意,小於1024的埠號必須要有管理員許可權才能繫結:

# 監聽埠:

s.bind(('127.0.0.1', 9999))

緊接著,呼叫listen()方法開始監聽埠,傳入的引數指定等待連線的最大數量:

s.listen(5)

print('waiting for connection...')

接下來,伺服器程式通過乙個永久迴圈來接受來自客戶端的連線,accept()會等待並返回乙個客戶端的連線:

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)

連線建立後,伺服器首先發一條歡迎訊息,然後等待客戶端資料,並加上hello再傳送給客戶端。如果客戶端傳送了exit字串,就直接關閉連線。

要測試這個伺服器程式,我們還需要編寫乙個客戶端程式:

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'michael', b'tracy', b'sarah']:

# 傳送資料:

s.send(data)

print(s.recv(1024).decode('utf-8'))

s.send(b'exit')

s.close()

我們需要開啟兩個命令列視窗,乙個執行伺服器程式,另乙個執行客戶端程式,就可以看到效果了:

│command prompt - □ x │

│$ python echo_server.py │

│waiting for connection... │

│accept new connection from 127.0.0.1:64398... │

│connection from 127.0.0.1:64398 closed. │

│ │command prompt - □ x │

│ │$ python echo_client.py │

│ │welcome! │

│ │hello, michael! │

└───────┤hello, tracy! │

│hello, sarah! │

需要注意的是,客戶端程式執行完畢就退出了,而伺服器程式會永遠執行下去,必須按ctrl+c退出程式。

小結用tcp協議進行socket程式設計在python中十分簡單,對於客戶端,要主動連線伺服器的ip和指定埠,對於伺服器,要首先監聽指定埠,然後,對每乙個新的連線,建立乙個執行緒或程序來處理。通常,伺服器程式會無限執行下去。

同乙個埠,被乙個socket繫結了以後,就不能被別的socket繫結了。

參考原始碼

程式設計,還是程式設計

喜歡程式設計,雖然水平一般,但還是執著地學習與程式設計有關的知識。中間因為工作關係與程式設計遠離了一段時間,現在又重拾起來,細想起來還是因為喜歡吧。喜歡程式軟體的思想和原理,喜歡程式 的魅力和成就感。程式設計軟體的思想是最值得學習的,一直認為思想決定行動,思想改變世界。每種軟體的流行和受人追捧,無不...

少兒程式設計程式設計

機械人比賽,聽上去讓人有一種高大上的科技感,沒錯,在大多數人眼裡,玩機械人那是科學家做的事情,不過隨著機械人教育的普及,越來越多的孩子也能夠駕馭這高大上的機械人。格物斯坦小坦克告訴你原因,這是歸結於孩子對於程式設計課程的學習,學會對機械人進行程式設計了,自然就能玩轉機械人啦。參加機械人比賽的意義遠遠...

LINUX程式設計 socket程式設計

什麼是套接字 套接字是一種通訊過程,它使客戶 伺服器系統的開發工作既可以在本地單機上進行,也可以跨網路進行。套接字建立過程 1,建立乙個套接字,這是分配給該伺服器程序的乙個作業系統資源,套接字由伺服器通過系統呼叫socket建立出來的,所以其它程序將不能對它進行訪問。2,給套接字起個名字,用系統呼叫...