首頁 > 軟體

Python網路程式設計之Python編寫TCP協定程式的步驟

2022-11-11 14:00:59

TCP使用者端程式開發

1. 開發 TCP 使用者端程式開發步驟回顧

  • 建立使用者端通訊端物件
  • 和伺服器端通訊端建立連線
  • 傳送資料
  • 接收資料
  • 關閉使用者端通訊端

2. socket 類的介紹

匯入 socket 模組 import socket

建立使用者端 socket 物件 socket.socket(AddressFamily, Type)

引數說明:

  • AddressFamily 表示IP地址型別, 分為TPv4和IPv6
  • Type 表示傳輸協定型別

方法說明:

  • connect((host, port)) 表示和伺服器端通訊端建立連線, host是伺服器ip地址,port是應用程式的埠號
  • send(data) 表示傳送資料,data是二進位制資料
  • recv(buffersize) 表示接收資料, buffersize是每次接收資料的長度

3. TCP 使用者端程式開發範例程式碼

import socket
if __name__ == '__main__':
    # 建立tcp使用者端通訊端
    # 1. AF_INET:表示ipv4
    # 2. SOCK_STREAM: tcp傳輸協定
    tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 和伺服器端應用程式建立連線
    tcp_client_socket.connect(("192.168.131.62", 8080))
    # 程式碼執行到此,說明連線建立成功
    # 準備傳送的資料
    send_data = "你好伺服器端,我是使用者端小黑!".encode("gbk")
    # 傳送資料
    tcp_client_socket.send(send_data)
    # 接收資料, 這次接收的資料最大位元組數是1024
    recv_data = tcp_client_socket.recv(1024)
    # 返回的直接是伺服器端程式傳送的二進位制資料
    print(recv_data)
    # 對資料進行解碼
    recv_content = recv_data.decode("gbk")
    print("接收伺服器端的資料為:", recv_content)
    # 關閉通訊端
    tcp_client_socket.close()

執行結果:

b'hello'
接收伺服器端的資料為: hello

說明

  • str.encode(編碼格式) 表示把字串編碼成為二進位制
  • data.decode(編碼格式) 表示把二進位制解碼成為字串

網路偵錯助手充當伺服器端程式:

TCP伺服器端程式開發

1. 開發 TCP 伺服器端程式開發步驟回顧

  • 建立伺服器端端通訊端物件
  • 繫結埠號
  • 設定監聽
  • 等待接受使用者端的連線請求
  • 接收資料
  • 傳送資料
  • 關閉通訊端

2. socket 類的介紹

匯入 socket 模組
import socket

建立伺服器端 socket 物件
socket.socket(AddressFamily, Type)

引數說明:

  • AddressFamily 表示IP地址型別, 分為TPv4和IPv6
  • Type 表示傳輸協定型別

方法說明:

  • bind((host, port)) 表示繫結埠號, host 是 ip 地址,port 是埠號,ip 地址一般不指定,表示本機的任何一個ip地址都可以。
  • listen (backlog) 表示設定監聽,backlog參數列示最大等待建立連線的個數。
  • accept() 表示等待接受使用者端的連線請求
  • send(data) 表示傳送資料,data 是二進位制資料
  • recv(buffersize) 表示接收資料, buffersize 是每次接收資料的長度

3. TCP 伺服器端程式開發範例程式碼

import socket
if __name__ == '__main__':
    # 建立tcp伺服器端通訊端
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 設定埠號複用,讓程式退出埠號立即釋放
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 
    # 給程式繫結埠號
    tcp_server_socket.bind(("", 8989))
    # 設定監聽
    # 128:最大等待建立連線的個數, 提示: 目前是單任務的伺服器端,同一時刻只能服務與一個使用者端,後續使用多工能夠讓伺服器端同時服務與多個使用者端,
    # 不需要讓使用者端進行等待建立連線
    # listen後的這個通訊端只負責接收使用者端連線請求,不能收發訊息,收發訊息使用返回的這個新通訊端來完成
    tcp_server_socket.listen(128)
    # 等待使用者端建立連線的請求, 只有使用者端和伺服器端建立連線成功程式碼才會解阻塞,程式碼才能繼續往下執行
    # 1. 專門和使用者端通訊的通訊端: service_client_socket
    # 2. 使用者端的ip地址和埠號: ip_port
    service_client_socket, ip_port = tcp_server_socket.accept()
    # 程式碼執行到此說明連線建立成功
    print("使用者端的ip地址和埠號:", ip_port)
    # 接收使用者端傳送的資料, 這次接收資料的最大位元組數是1024
    recv_data = service_client_socket.recv(1024)
    # 獲取資料的長度
    recv_data_length = len(recv_data)
    print("接收資料的長度為:", recv_data_length)
    # 對二進位制資料進行解碼
    recv_content = recv_data.decode("gbk")
    print("接收使用者端的資料為:", recv_content)
    # 準備傳送的資料
    send_data = "ok, 問題正在處理中...".encode("gbk")
    # 傳送資料給使用者端
    service_client_socket.send(send_data)
    # 關閉服務與使用者端的通訊端, 終止和使用者端通訊的服務
    service_client_socket.close()
    # 關閉伺服器端的通訊端, 終止和使用者端提供建立連線請求的服務
    tcp_server_socket.close()

執行結果:

使用者端的ip地址和埠號: ('172.16.47.209', 52472)
接收資料的長度為: 5
接收使用者端的資料為: hello

說明:

  • 更換伺服器端埠號
  • 設定埠號複用(推薦大家使用),也就是說讓伺服器端程式退出後埠號立即釋放。

解決辦法有兩種:

更換伺服器端埠號設定埠號複用(推薦大家使用),也就是說讓伺服器端程式退出後埠號立即釋放。

設定埠號複用的程式碼如下:

# 引數1: 表示當前通訊端
# 引數2: 設定埠號複用選項
# 引數3: 設定埠號複用選項對應的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

網路偵錯助手充當使用者端程式:

TCP網路應用程式的注意點

  • 當 TCP 使用者端程式想要和 TCP 伺服器端程式進行通訊的時候必須要先建立連線
  • TCP 使用者端程式一般不需要繫結埠號,因為使用者端是主動發起建立連線的。
  • TCP 伺服器端程式必須繫結埠號,否則使用者端找不到這個 TCP 伺服器端程式。
  • listen 後的通訊端是被動通訊端,只負責接收新的使用者端的連線請求,不能收發訊息。
  • 當 TCP 使用者端程式和 TCP 伺服器端程式連線成功後, TCP 伺服器端程式會產生一個新的通訊端,收發使用者端訊息使用該通訊端。
  • 關閉 accept 返回的通訊端意味著和這個使用者端已經通訊完畢。
  • 關閉 listen 後的通訊端意味著伺服器端的通訊端關閉了,會導致新的使用者端不能連線伺服器端,但是之前已經接成功的使用者端還能正常通訊。
  • 當用戶端的通訊端呼叫 close 後,伺服器端的 recv 會解阻塞,返回的資料長度為0,伺服器端可以通過返回資料的長度來判斷使用者端是否已經下線,反之伺服器端關閉通訊端,使用者端的 recv 也會解阻塞,返回的資料長度也為0。

案例:多工版TCP伺服器端程式開發

1. 需求

目前我們開發的TCP伺服器端程式只能服務於一個使用者端,如何開發一個多工版的TCP伺服器端程式能夠服務於多個使用者端呢?

完成多工,可以使用執行緒,比程序更加節省記憶體資源。

2. 具體實現步驟

  • 編寫一個TCP伺服器端程式,迴圈等待接受使用者端的連線請求
  • 當用戶端和伺服器端建立連線成功,建立子執行緒,使用子執行緒專門處理使用者端的請求,防止主執行緒阻塞
  • 把建立的子執行緒設定成為守護主執行緒,防止主執行緒無法退出。

3. 多工版TCP伺服器端程式的範例程式碼:

import socket
import threading
 
 
# 處理使用者端的請求操作
def handle_client_request(service_client_socket, ip_port):
    # 迴圈接收使用者端傳送的資料
    while True:
        # 接收使用者端傳送的資料
        recv_data = service_client_socket.recv(1024)
        # 容器型別判斷是否有資料可以直接使用if語句進行判斷,如果容器型別裡面有資料表示條件成立,否則條件失敗
        # 容器型別: 列表、字典、元組、字串、set、range、二進位制資料
        if recv_data:
            print(recv_data.decode("gbk"), ip_port)
            # 回覆
            service_client_socket.send("ok,問題正在處理中...".encode("gbk"))
 
        else:
            print("使用者端下線了:", ip_port)
            break
    # 終止和使用者端進行通訊
    service_client_socket.close()
 
 
if __name__ == '__main__':
    # 建立tcp伺服器端通訊端
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 設定埠號複用,讓程式退出埠號立即釋放
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 繫結埠號
    tcp_server_socket.bind(("", 9090))
    # 設定監聽, listen後的通訊端是被動通訊端,只負責接收使用者端的連線請求
    tcp_server_socket.listen(128)
    # 迴圈等待接收使用者端的連線請求
    while True:
        # 等待接收使用者端的連線請求
        service_client_socket, ip_port = tcp_server_socket.accept()
        print("使用者端連線成功:", ip_port)
        # 當用戶端和伺服器端建立連線成功以後,需要建立一個子執行緒,不同子執行緒負責接收不同使用者端的訊息
        sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
        # 設定守護主執行緒
        sub_thread.setDaemon(True)
        # 啟動子執行緒
        sub_thread.start()
 
 
    # tcp伺服器端通訊端可以不需要關閉,因為伺服器端程式需要一直執行
    # tcp_server_socket.close()

執行結果:

使用者端連線成功: ('172.16.47.209', 51528)
使用者端連線成功: ('172.16.47.209', 51714)
hello1 ('172.16.47.209', 51528)
hello2 ('172.16.47.209', 51714)

socket的send和recv原理剖析

1. 認識TCP socket的傳送和接收緩衝區

當建立一個TCP socket物件的時候會有一個傳送緩衝區和一個接收緩衝區這個傳送和接收緩衝區指的就是記憶體中的一片空間。

2. send原理剖析

send是不是直接把資料發給伺服器端?

不是,要想發資料,必須得通過網路卡傳送資料,應用程式是無法直接通過網路卡傳送資料的,它需要呼叫作業系統介面,也就是說,應用程式把傳送的資料先寫入到傳送緩衝區(記憶體中的一片空間),再由作業系統控制網路卡把傳送緩衝區的資料傳送給伺服器端網路卡 

3. recv原理剖析

recv是不是直接從使用者端接收資料?

不是,應用軟體是無法直接通過網路卡接收資料的,它需要呼叫作業系統介面,由作業系統通過網路卡接收資料,把接收的資料寫入到接收緩衝區(記憶體中的一片空間),應用程式再從接收快取區獲取使用者端傳送的資料

4. send和recv原理剖析圖

說明:

  • 傳送資料是傳送到傳送緩衝區
  • 接收資料是從接收緩衝區 獲取

到此這篇關於Python網路程式設計(二)編寫TCP協定程式的文章就介紹到這了,更多相關Python TCP協定程式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com