01. Bing 提供的範例
展示如何與位於 IP 地址 192.168.1.1 和端口 2000 的 PLC 進行連線,以及如何從 PLC 讀取資料和向 PLC 寫入資料。
檔案: socket_0307-1.py
import socket
# 設定伺服器的 IP 地址和端口號
server_ip = '192.168.1.1'
server_port = 2000
# 建立一個 socket 物件
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 連線到伺服器
client_socket.connect((server_ip, server_port))
# 向伺服器發送資料
client_socket.sendall("Hello, PLC!".encode())
# 從伺服器接收資料
data = client_socket.recv(1024)
# 打印接收到的資料
print('Received from the server:', data.decode())
# 關閉連線
client_socket.close()
02. 伺服器端
我們將在本機建置一個伺服器與客戶端,將對客戶端接收與傳送訊息。
(2-1) 載入套件
由於客戶數量可能會很大,因此我們需要使用 threading 多執行緒。
Python 網路程式設計_Socket內建方法
Python 提供了兩個級別訪問的網路服務:
- 低級別的網路服務支援基本的Socket,它提供了標準的BSD Sockets API,可以訪問底層操作系統Socket介面的全部方法。
- 高級別的網路服務模組 SocketServer, 它提供了伺服器中心類,可以簡化網路伺服器的開發。
什麼是Socket?
Socket又稱「套接字」,應用程式通常通過「套接字」向網路發出請求或者應答網路請求,使主機間或者一台電腦上的進程間可以通訊。
socket()函數
Python 中,我們用 socket() 函數來創建套接字,語法格式如下:
socket.socket([family[, type[, proto]]])
參數
- family: 套接字家族可以使 AF_UNIX 或者 AF_INET。
- type: 套接字類型可以根據是面向連接的還是非連接分為 SOCK_STREAM 或 SOCK_DGRAM。
- protocol: 一般不填預設為 0。
簡單實例
(1) 服務端
我們使用 socket 模組的 socket 函數來建立一個 socket 物件。 socket 物件可以透過呼叫其他函數來設定一個 socket 服務。
現在我們可以透過呼叫 bind(hostname, port) 函數來指定服務的 port(連接埠)。
接著,我們呼叫 socket 物件的 accept 方法。 此方法等待客戶端的連接,並傳回 connection 對象,表示已連接到客戶端。
檔案: server.py
import socket
s = socket.socket() # 創建 socket 對象
host = socket.gethostname() # 獲取本地主機名
port = 12345
s.bind((host, port)) # 绑定端口
s.listen(5) # 等待客户端连接
while True:
c,addr = s.accept() # 建立客户端连接
print(f"連接地址: {addr}")
c.send("歡迎訪問菜鳥教程!")
c.close() # 關閉連接
(2) 客戶端
接下來我們寫一個簡單的客戶端實例連接到以上所建立的服務。 連接埠號碼為 12345。
socket.connect(hostname, port ) 方法開啟一個 TCP 連線到主機為 hostname 連接埠為 port 的服務商。 連線後我們就可以從服務端取得數據,記住,操作完成後需要關閉連線。
檔案: client.py
import socket
s = socket.socket() # 創建 socket 對象
host = socket.gethostname() # 獲取本地主機名
port = 12345
s.connect((host, port))
print(s.recv(1024))
s.close()
(2-2) 檢查網路狀態
輸入 ipconfig 檢查目前的網路狀態。IPv4 是目前網路的位置。
(2-3) 獲取當前主機IP
使用 socket
模組來獲取當前主機的IP地址。gethostname()
函數返回當前執行程式的系統的主機名,而 gethostbyname()
函數將這個主機名轉換成IP地址。
檔案: server.py
import socket
import threading
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
(2-4) 綁定socket位址
socket.AF_INET
是一個地址族(Address Family),指定了這個socket將使用IPv4網路協議。socket.SOCK_STREAM
表示socket類型。SOCK_STREAM
代表這個socket將使用TCP協議,這是一種連接導向的、可靠的、基於字節流的傳輸方式。
檔案: server.py
import socket
import threading
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
# print(SERVER) #127.0.0.1
# print(socket.gethostname()) #23-0535487-H1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
(2-5) server accept設置
conn
和 addr
是由 server.accept()
返回的兩個值:
conn
:這是一個新的socket物件,用於與客戶端進行通信。你可以使用它來傳送和接收數據。addr
:這是一個包含客戶端地址的元組。它包含客戶端的IP地址和埠號。例如,如果客戶端從IP地址192.168.1.10
的埠號12345
連接,則addr
的值將是("192.168.1.10", 12345)
。
因此,conn
是一個socket物件,addr
是一個元組,包含了客戶端的地址信息。你可以在 handle_client(conn, addr)
函式中使用這些值來處理客戶端的請求。
檔案: server.py
import socket
import threading
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
# AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP協議
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 將伺服器綁定到指定的IP地址和埠號
server.bind(ADDR)
def handle_client(conn, addr):
pass
def start():
# (2-5)server accept設置
# start() 函式是一個無限迴圈,它會等待客戶端的連接。
# 當有客戶端連接時,server.accept()會返回一個新的socket物件(conn)和客戶端的地址(addr)。
# 你可以在handle_client(conn, addr)函式中處理客戶端的請求。
server.listen()
while True:
conn, addr = server.accept()
print("[STARTING] server已經啟動....")
start()
(2-6) 啟動多線程
每當有新的客戶端連接時,它會創建一個新的線程來處理該客戶端的請求。
檔案: server.py
import socket
import threading
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
# AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP協議
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 將伺服器綁定到指定的IP地址和埠號
server.bind(ADDR)
def handle_client(conn, addr):
pass
def start():
# (2-5)server accept設置
# start() 函式是一個無限迴圈,它會等待客戶端的連接。
# 當有客戶端連接時,server.accept()會返回一個新的socket物件(conn)和客戶端的地址(addr)。
# 你可以在handle_client(conn, addr)函式中處理客戶端的請求。
server.listen()
while True:
conn, addr = server.accept()
# (2-6)啟動多線程
# 將conn和addr傳遞給handle_client函式作為參數
thread = threading.Thread(target=handle_client, args=(conn, addr))
# 啟動線程,開始執行 handle_client 函式
thread.start()
# 打印出當前活動的線程數量(不包括主線程)
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server已經啟動....")
start()
(2-7) 處理客戶端的連接
檔案: server.py
import socket
import threading
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
# AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP協議
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 將伺服器綁定到指定的IP地址和埠號
server.bind(ADDR)
def handle_client(conn, addr):
# (2-7)用於處理客戶端的連接
# 顯示客戶端的地址(addr)
print(f"[NEW ONNECTION] {addr} 已連接.")
connected = True
while connected:
# 在收到客戶端的消息前,不會運行。用於接收客戶端發送的數據。一旦有數據到達,它會返回該數據。
msg = conn.recv() #阻塞運行碼
def start():
# (2-5)server accept設置
# start() 函式是一個無限迴圈,它會等待客戶端的連接。
# 當有客戶端連接時,server.accept()會返回一個新的socket物件(conn)和客戶端的地址(addr)。
# 你可以在handle_client(conn, addr)函式中處理客戶端的請求。
server.listen()
while True:
conn, addr = server.accept()
# (2-6)啟動多線程
# 將conn和addr傳遞給handle_client函式作為參數
thread = threading.Thread(target=handle_client, args=(conn, addr))
# 啟動線程,開始執行 handle_client 函式
thread.start()
# 打印出當前活動的線程數量(不包括主線程)
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server已經啟動....")
start()
(2-8) 字節格式解碼為字符串
msg_length = conn.recv(HEADER).decode(FORMAT)
:- 這行程式碼會從客戶端接收一個標頭,該標頭指示了即將接收的消息的長度。
HEADER
是一個常數,通常是一個固定的整數,用於指定標頭的大小。conn.recv(HEADER)
會阻塞運行,直到接收到指定大小的數據。decode(FORMAT)
用於將接收到的字節數據解碼為字符串。
msg_length = int(msg_length)
:- 將接收到的消息長度轉換為整數。
msg = conn.recv(msg_length).decode(FORMAT)
:- 這行程式碼會接收實際的消息,其長度由上一步計算得出。
conn.recv(msg_length)
會阻塞運行,直到接收到指定長度的數據。decode(FORMAT)
用於將接收到的字節數據解碼為字符串。
print(f"[{addr}] {msg}")
:- 這行程式碼會打印出客戶端的地址(
addr
)和接收到的消息(msg
)。
- 這行程式碼會打印出客戶端的地址(
檔案: server.py
import socket
import threading
# (2-8)字節格式解碼為字符串
# 固定長度的標頭
HEADER = 64
FORMAT = 'utf-8'
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
# AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP協議
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 將伺服器綁定到指定的IP地址和埠號
server.bind(ADDR)
def handle_client(conn, addr):
# (2-7)用於處理客戶端的連接
# 顯示客戶端的地址(addr)
print(f"[NEW ONNECTION] {addr} 已連接.")
connected = True
while connected:
# 在收到客戶端的消息前,不會運行。用於接收客戶端發送的數據。一旦有數據到達,它會返回該數據。
# msg = conn.recv() #阻塞運行碼
# (2-8)字節格式解碼為字符串
# 消息長度等於conn點收到的標頭。 因為每次我們發送消息時,
# 都需要編碼為字節格式,所以將字節格式解碼為字符串。
msg_length = conn.recv(HEADER).decode(FORMAT)
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
print(f"[{addr}] {msg}")
def start():
# (2-5)server accept設置
# start() 函式是一個無限迴圈,它會等待客戶端的連接。
# 當有客戶端連接時,server.accept()會返回一個新的socket物件(conn)和客戶端的地址(addr)。
# 你可以在handle_client(conn, addr)函式中處理客戶端的請求。
server.listen()
while True:
conn, addr = server.accept()
# (2-6)啟動多線程
# 將conn和addr傳遞給handle_client函式作為參數
thread = threading.Thread(target=handle_client, args=(conn, addr))
# 啟動線程,開始執行 handle_client 函式
thread.start()
# 打印出當前活動的線程數量(不包括主線程)
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server已經啟動....")
start()
(2-9) 自動斷開連接
在接收到特定消息(DISCONNECT_MESSAGE
)時,程式碼會自動斷開與客戶端的連接,將 connected
設置為 False
。
檔案: server.py
import socket
import threading
# (2-8)字節格式解碼為字符串
# 固定長度的標頭
HEADER = 64
FORMAT = 'utf-8'
# (2-9)自動斷開連接
DISCONNECT_MESSAGE = "!DISCONNECT"
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
# AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP協議
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 將伺服器綁定到指定的IP地址和埠號
server.bind(ADDR)
def handle_client(conn, addr):
# (2-7)用於處理客戶端的連接
# 顯示客戶端的地址(addr)
print(f"[NEW ONNECTION] {addr} 已連接.")
connected = True
while connected:
# 在收到客戶端的消息前,不會運行。用於接收客戶端發送的數據。一旦有數據到達,它會返回該數據。
# msg = conn.recv() #阻塞運行碼
# (2-8)字節格式解碼為字符串
# 消息長度等於conn點收到的標頭。 因為每次我們發送消息時,
# 都需要編碼為字節格式,所以將字節格式解碼為字符串。
msg_length = conn.recv(HEADER).decode(FORMAT)
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
# (2-9)自動斷開連接
if msg == DISCONNECT_MESSAGE:
connected = False
print(f"[{addr}] {msg}")
conn.close()
def start():
# (2-5)server accept設置
# start() 函式是一個無限迴圈,它會等待客戶端的連接。
# 當有客戶端連接時,server.accept()會返回一個新的socket物件(conn)和客戶端的地址(addr)。
# 你可以在handle_client(conn, addr)函式中處理客戶端的請求。
server.listen()
while True:
conn, addr = server.accept()
# (2-6)啟動多線程
# 將conn和addr傳遞給handle_client函式作為參數
thread = threading.Thread(target=handle_client, args=(conn, addr))
# 啟動線程,開始執行 handle_client 函式
thread.start()
# 打印出當前活動的線程數量(不包括主線程)
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server已經啟動....")
start()
03. 客戶端
(3-1) 帶入同SERVER參數
檔案: client.py
import socket
# (3-1)帶入同SERVER參數
# 輸入跟SERVER端相同的參數
HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
# SERVER的IP要根據實際運行的輸入
SERVER = "192.168.201.72"
ADDR = (SERVER, PORT)
(3-2) 建立客戶端連接到指定的服務器地址
我們獲得了連接 1。
建立一個客戶端(client)的套接字(socket)並連接到指定的服務器地址(ADDR
)。讓我們來看看它的功能:
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
:這行程式碼創建了一個新的套接字對象。其中,socket.AF_INET
表示使用 IPv4 地址族,socket.SOCK_STREAM
表示使用 TCP 協議。這個套接字將用於與服務器進行通信。client.connect(ADDR)
:這行程式碼連接到指定的服務器地址(ADDR
)。它建立了客戶端與服務器之間的通信通道,以便後續的數據交換。
建立一個客戶端套接字並連接到指定的服務器地址,以便進行通信。
檔案: client.py
import socket
# (3-1)帶入同SERVER參數
# 輸入跟SERVER端相同的參數
HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
# SERVER的IP要根據實際運行的輸入
SERVER = "192.168.201.72"
ADDR = (SERVER, PORT)
# (3-2)建立客戶端連接到指定的服務器地址
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
檔案: server.py
import socket
import threading
# (2-8)字節格式解碼為字符串
# 固定長度的標頭
HEADER = 64
FORMAT = 'utf-8'
# (2-9)自動斷開連接
DISCONNECT_MESSAGE = "!DISCONNECT"
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
# AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP協議
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 將伺服器綁定到指定的IP地址和埠號
server.bind(ADDR)
def handle_client(conn, addr):
# (2-7)用於處理客戶端的連接
# 顯示客戶端的地址(addr)
print(f"[NEW ONNECTION] {addr} 已連接.")
connected = True
while connected:
# 在收到客戶端的消息前,不會運行。用於接收客戶端發送的數據。一旦有數據到達,它會返回該數據。
# msg = conn.recv() #阻塞運行碼
# (2-8)字節格式解碼為字符串
# 消息長度等於conn點收到的標頭。 因為每次我們發送消息時,
# 都需要編碼為字節格式,所以將字節格式解碼為字符串。
msg_length = conn.recv(HEADER).decode(FORMAT)
# (3-2)建立客戶端連接到指定的服務器地址
# 判斷是否有收到msg_length。因為第一次不會收到任何內容。
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
# (2-9)自動斷開連接
if msg == DISCONNECT_MESSAGE:
connected = False
print(f"[{addr}] {msg}")
conn.close()
def start():
# (2-5)server accept設置
# start() 函式是一個無限迴圈,它會等待客戶端的連接。
# 當有客戶端連接時,server.accept()會返回一個新的socket物件(conn)和客戶端的地址(addr)。
# 你可以在handle_client(conn, addr)函式中處理客戶端的請求。
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
conn, addr = server.accept()
# (2-6)啟動多線程
# 將conn和addr傳遞給handle_client函式作為參數
thread = threading.Thread(target=handle_client, args=(conn, addr))
# 啟動線程,開始執行 handle_client 函式
thread.start()
# 打印出當前活動的線程數量(不包括主線程)
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server已經啟動....")
start()
(3-3) 將訊息傳送給server端
send 之所以分兩次傳送,是為了確保訊息的正確接收。
讓我們來看看為什麼這麼做:
- 訊息長度傳送:首先,程式碼將訊息的長度(
msg_length
)以字串形式傳送給客戶端。這樣客戶端就知道接下來要接收多少位元組的訊息。這是一個必要的步驟,因為不同的訊息可能有不同的長度。 - 訊息本體傳送:接著,程式碼再次使用
client.send
函式,將編碼後的訊息本體傳送給客戶端。這樣客戶端就能夠解碼並處理訊息。
總之,分兩次傳送的目的是確保訊息的完整性和正確性。第一次傳送訊息長度,第二次傳送訊息本體,以確保客戶端能夠正確接收並處理訊息。
檔案: client.py
import socket
# (3-1)帶入同SERVER參數
# 輸入跟SERVER端相同的參數
HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
# SERVER的IP要根據實際運行的輸入
SERVER = "192.168.201.72"
ADDR = (SERVER, PORT)
# (3-2)建立客戶端連接到指定的服務器地址
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
# (3-3)將訊息傳送給server端
def send(msg):
# 以指定的編碼格式(FORMAT)進行編碼
message = msg.encode(FORMAT)
# 計算訊息的長度,並將其轉換為字串,再次以相同的編碼格式進行編碼
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
# 補足訊息長度,使其達到指定的標頭長度(HEADER)
send_length += b' ' * (HEADER - len(send_length))
# 使用 client.send 函式兩次,分別傳送訊息的長度和編碼後的訊息本體
client.send(send_length)
client.send(message)
send("Hello World!")
(3-4) 一次傳送多組訊息
檔案: client.py
import socket
# (3-1)帶入同SERVER參數
# 輸入跟SERVER端相同的參數
HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
# SERVER的IP要根據實際運行的輸入
SERVER = "192.168.201.72"
ADDR = (SERVER, PORT)
# (3-2)建立客戶端連接到指定的服務器地址
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
# (3-3)將訊息傳送給server端
def send(msg):
# 以指定的編碼格式(FORMAT)進行編碼
message = msg.encode(FORMAT)
# 計算訊息的長度,並將其轉換為字串,再次以相同的編碼格式進行編碼
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
# 補足訊息長度,使其達到指定的標頭長度(HEADER)
send_length += b' ' * (HEADER - len(send_length))
# 使用 client.send 函式兩次,分別傳送訊息的長度和編碼後的訊息本體
client.send(send_length)
client.send(message)
# (3-4)一次傳送多組訊息
send("Hello World!")
send("Hello Everyone!")
send("Hello Tim!")
send(DISCONNECT_MESSAGE)
04. 從伺服端向客戶端發送消息
(4-1) Server 回覆 Client
我們希望 Server 在收到訊息後,回覆給 client 收到的訊息。
檔案: server.py
import socket
import threading
# (2-8)字節格式解碼為字符串
# 固定長度的標頭
HEADER = 64
FORMAT = 'utf-8'
# (2-9)自動斷開連接
DISCONNECT_MESSAGE = "!DISCONNECT"
# (2-3)獲取當前主機IP
PORT = 5050
# SERVER = "192.168.1.26"
SERVER = socket.gethostbyname(socket.gethostname())
print(SERVER) #127.0.0.1
# (2-4)綁定socket位址
ADDR = (SERVER, PORT)
# 創建一個新的socket
# AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP協議
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 將伺服器綁定到指定的IP地址和埠號
server.bind(ADDR)
def handle_client(conn, addr):
# (2-7)用於處理客戶端的連接
# 顯示客戶端的地址(addr)
print(f"[NEW ONNECTION] {addr} 已連接.")
connected = True
while connected:
# 在收到客戶端的消息前,不會運行。用於接收客戶端發送的數據。一旦有數據到達,它會返回該數據。
# msg = conn.recv() #阻塞運行碼
# (2-8)字節格式解碼為字符串
# 消息長度等於conn點收到的標頭。 因為每次我們發送消息時,
# 都需要編碼為字節格式,所以將字節格式解碼為字符串。
msg_length = conn.recv(HEADER).decode(FORMAT)
# (3-2)建立客戶端連接到指定的服務器地址
# 判斷是否有收到msg_length。 因為第一次不會收到任何內容。
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
# (2-9)自動斷開連接
if msg == DISCONNECT_MESSAGE:
connected = False
print(f"[{addr}] {msg}")
# (4-1)server送訊息給client
# conn.send("Msg Received".encode(FORMAT))
conn.send("訊息已收到".encode(FORMAT))
conn.close()
def start():
# (2-5)server accept設置
# start() 函式是一個無限迴圈,它會等待客戶端的連接。
# 當有客戶端連接時,server.accept()會返回一個新的socket物件(conn)和客戶端的地址(addr)。
# 你可以在handle_client(conn, addr)函式中處理客戶端的請求。
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
conn, addr = server.accept()
# (2-6)啟動多線程
# 將conn和addr傳遞給handle_client函式作為參數
thread = threading.Thread(target=handle_client, args=(conn, addr))
# 啟動線程,開始執行 handle_client 函式
thread.start()
# 打印出當前活動的線程數量(不包括主線程)
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server已經啟動....")
start()
理論上 recv(2048) 的值應該跟 HEAD 長度相同,但也可以留下更大的空間。
檔案: client.py
import socket
# (3-1)帶入同SERVER參數
# 輸入跟SERVER端相同的參數
HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
# SERVER的IP要根據實際運行的輸入
SERVER = "192.168.201.72"
ADDR = (SERVER, PORT)
# (3-2)建立客戶端連接到指定的服務器地址
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
# (3-3)將訊息傳送給server端
def send(msg):
# 以指定的編碼格式(FORMAT)進行編碼
message = msg.encode(FORMAT)
# 計算訊息的長度,並將其轉換為字串,再次以相同的編碼格式進行編碼
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
# 補足訊息長度,使其達到指定的標頭長度(HEADER)
send_length += b' ' * (HEADER - len(send_length))
# 使用 client.send 函式兩次,分別傳送訊息的長度和編碼後的訊息本體
client.send(send_length)
client.send(message)
# (4-1)server送訊息給client
# print(client.recv(2048))
# 進行解碼確保不是字節
print(client.recv(2048).decode(FORMAT))
# (3-4)一次傳送多組訊息
send("Hello World!")
send("Hello Everyone!")
send("Hello Tim!")
send(DISCONNECT_MESSAGE)
..