介绍
Python sockets 提供了强大的网络通信工具,让你能够在不同的系统之间发送和接收消息。在本教程中,我们将引导你使用 Python sockets 建立连接、传输数据和接收响应。在本次实验(Lab)结束时,你将构建一个客户端和服务器应用程序,它们可以通过网络相互通信。
这些实践知识构成了开发更复杂的网络应用程序的基础,从聊天程序到分布式系统。
Python sockets 提供了强大的网络通信工具,让你能够在不同的系统之间发送和接收消息。在本教程中,我们将引导你使用 Python sockets 建立连接、传输数据和接收响应。在本次实验(Lab)结束时,你将构建一个客户端和服务器应用程序,它们可以通过网络相互通信。
这些实践知识构成了开发更复杂的网络应用程序的基础,从聊天程序到分布式系统。
让我们从理解什么是 sockets 以及它们在 Python 中的工作方式开始。然后,我们将创建我们的第一个 socket,看看它是如何初始化的。
Sockets 是通过网络发送和接收数据的端点。它们提供了一个编程接口,用于网络通信,允许应用程序交换信息,而不管它们的位置如何。
在网络编程中,我们通常使用客户端 - 服务器模型:
Python 内置的 socket 模块使得使用 sockets 变得很容易,而无需了解网络协议的所有复杂细节。
两种最常见的 socket 类型是:
对于本次实验(Lab),我们将重点关注 TCP sockets,这是应用程序需要可靠通信时最常用的类型。
让我们创建一个简单的 Python 脚本来初始化一个 socket。打开 WebIDE 并按照以下步骤操作:
mkdir -p ~/project/socket_lab
cd ~/project/socket_lab
socket_basics.py 的新 Python 文件:在 WebIDE 中,单击“新建文件”按钮或使用“文件”菜单并选择“新建文件”,然后在 socket_lab 目录中将其命名为 socket_basics.py。
import socket
## Creating a socket
print("Creating a new socket...")
my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(f"Socket created: {my_socket}")
## Explaining the parameters:
print("\nSocket parameters explained:")
print("AF_INET: Using IPv4 addressing")
print("SOCK_STREAM: Using TCP protocol for reliable data transmission")
## Getting available socket methods
print("\nSome methods available on socket objects:")
methods = [method for method in dir(my_socket) if not method.startswith('_')]
print(', '.join(methods[:10]) + '...') ## Showing first 10 methods
## Closing the socket
my_socket.close()
print("\nSocket closed")
保存文件。
运行脚本以查看输出:
python3 ~/project/socket_lab/socket_basics.py
你应该看到类似于这样的输出:
Creating a new socket...
Socket created: <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
Socket parameters explained:
AF_INET: Using IPv4 addressing
SOCK_STREAM: Using TCP protocol for reliable data transmission
Some methods available on socket objects:
accept, bind, close, connect, connect_ex, detach, fileno, getpeername, getsockname, getsockopt...
Socket closed
此输出显示我们已成功创建了一个 socket 对象并探索了它的一些属性。在接下来的部分中,我们将使用 sockets 构建一个服务器和客户端应用程序,它们可以相互通信。
在继续之前,让我们了解一些我们将要使用的关键 socket 函数:
socket() - 创建一个新的 socket 对象bind() - 将 socket 与特定的网络接口和端口关联起来listen() - 允许服务器接受连接accept() - 接受来自客户端的连接connect() - 连接到远程地址send() - 将数据发送到已连接的 socketrecv() - 从已连接的 socket 接收数据close() - 关闭 socket在下一步中,我们将使用这些函数创建一个服务器。
现在我们了解了 sockets 的基础知识,让我们创建一个简单的服务器,它监听连接并从客户端接收消息。
一个 socket 服务器遵循以下一般步骤:
让我们在 Python 中实现这种模式。
socket_lab 目录中创建一个名为 server.py 的新 Python 文件:在 WebIDE 中,单击“新建文件”按钮或使用“文件”菜单并选择“新建文件”,然后在 socket_lab 目录中将其命名为 server.py。
import socket
def start_server():
## Server configuration
host = '127.0.0.1' ## localhost
port = 12345 ## arbitrary non-privileged port
## Create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## Set socket option to reuse address (helps avoid "Address already in use" errors)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
## Bind socket to address and port
server_socket.bind((host, port))
## Listen for connections (queue up to 5 connection requests)
server_socket.listen(5)
print(f"Server started on {host}:{port}")
print("Waiting for client connection...")
try:
## Accept connection
client_socket, client_address = server_socket.accept()
print(f"Connection established with {client_address}")
## Receive data from client
data = client_socket.recv(1024) ## receive up to 1024 bytes
print(f"Message received: {data.decode()}")
## Send response to client
response = "Message received by server"
client_socket.send(response.encode())
## Close client connection
client_socket.close()
except KeyboardInterrupt:
print("\nServer shutting down...")
finally:
## Close server socket
server_socket.close()
print("Server socket closed")
if __name__ == "__main__":
start_server()
让我们分解一下服务器的关键组件:
Socket 创建:我们使用 socket.socket() 创建一个 TCP socket,使用 AF_INET (IPv4) 和 SOCK_STREAM (TCP 协议)。
Socket 选项:我们设置一个选项来重用地址,这有助于防止在关闭服务器后快速重启服务器时出现“地址已在使用中”的错误。
绑定:我们将 socket 绑定到地址 127.0.0.1 (localhost) 和端口 12345。这告诉操作系统我们希望在此特定的网络位置接收连接。
监听:listen(5) 调用告诉 socket 在拒绝新连接之前,最多排队 5 个连接请求。
接受连接:accept() 方法会阻塞(等待),直到客户端连接,然后返回一个新的 socket 对象,用于与该客户端通信,以及客户端的地址。
接收数据:我们使用 recv(1024) 从客户端接收最多 1024 字节的数据。数据以字节形式出现,因此我们使用 decode() 将其转换为字符串。
发送数据:我们使用 send() 方法将响应发送回客户端。我们 encode() 字符串将其转换为字节,然后再发送。
关闭:最后,我们关闭客户端 socket 和服务器 socket 以释放资源。
让我们运行我们的服务器,看看它的实际效果。由于我们还没有创建客户端,所以它还不会做太多事情,但我们可以验证它是否正确启动:
python3 ~/project/socket_lab/server.py
你应该看到这样的输出:
Server started on 127.0.0.1:12345
Waiting for client connection...
服务器现在正在等待客户端连接。由于我们还没有客户端,请按 Ctrl+C 停止服务器:
^C
Server shutting down...
Server socket closed
在下一步中,我们将创建一个客户端来连接到我们的服务器。
现在我们有了服务器,我们需要创建一个可以连接到它并发送消息的客户端。让我们构建一个简单的客户端应用程序。
一个 socket 客户端遵循以下一般步骤:
socket_lab 目录中创建一个名为 client.py 的新 Python 文件:在 WebIDE 中,单击“新建文件”按钮或使用“文件”菜单并选择“新建文件”,然后在 socket_lab 目录中将其命名为 client.py。
import socket
def start_client():
## Server information to connect to
host = '127.0.0.1' ## localhost - same as server
port = 12345 ## same port as server
try:
## Create socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## Connect to server
print(f"Connecting to server at {host}:{port}...")
client_socket.connect((host, port))
print("Connected to server")
## Send a message
message = "Hello from the client!"
print(f"Sending message: {message}")
client_socket.send(message.encode())
## Receive response
response = client_socket.recv(1024)
print(f"Response from server: {response.decode()}")
except ConnectionRefusedError:
print("Connection failed. Make sure the server is running.")
except Exception as e:
print(f"An error occurred: {e}")
finally:
## Close socket
client_socket.close()
print("Connection closed")
if __name__ == "__main__":
start_client()
让我们检查一下客户端代码的关键部分:
Socket 创建:与服务器类似,我们使用 socket.socket() 创建一个 TCP socket。
连接:客户端使用 connect() 建立与服务器在指定主机和端口上的连接,而不是绑定和监听。
发送数据:我们使用 send() 方法发送消息,确保将字符串编码为字节。
接收数据:我们使用 recv(1024) 接收服务器的响应,并将其解码回字符串。
错误处理:我们包括错误处理以捕获常见问题,例如服务器不可用 (ConnectionRefusedError)。
关闭:完成后,我们关闭 socket 以释放资源。
现在让我们一起测试我们的客户端和服务器。我们需要在单独的终端窗口中运行它们。
python3 ~/project/socket_lab/server.py
你应该看到:
Server started on 127.0.0.1:12345
Waiting for client connection...
python3 ~/project/socket_lab/client.py
你应该在客户端终端中看到类似这样的输出:
Connecting to server at 127.0.0.1:12345...
Connected to server
Sending message: Hello from the client!
Response from server: Message received by server
Connection closed
而在服务器终端中,你应该看到:
Connection established with ('127.0.0.1', 55234) ## The port number may differ
Message received: Hello from the client!
在客户端断开连接后,服务器将停止,因为我们当前的实现只处理单个连接。如果服务器仍在运行,请在服务器终端中按 Ctrl+C 将其关闭。
这演示了使用 Python sockets 在客户端和服务器之间成功通信。客户端能够向服务器发送消息,并且服务器可以接收它并将响应发送回去。
我们当前的服务器 - 客户端实现仅在关闭之前处理单个消息交换。大多数实际应用需要保持连接并处理多条消息。让我们增强我们的代码以创建更具交互性的体验。
首先,让我们修改服务器以持续接受连接并处理来自每个客户端的多条消息。
server.py 文件,并将代码替换为:import socket
def start_server():
## Server configuration
host = '127.0.0.1'
port = 12345
## Create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
## Bind and listen
server_socket.bind((host, port))
server_socket.listen(5)
print(f"Server running on {host}:{port}")
print("Press Ctrl+C to stop the server")
while True: ## Continuous server loop
print("\nWaiting for a connection...")
client_socket, client_address = server_socket.accept()
print(f"Connected to client: {client_address}")
## Handle client communication
handle_client(client_socket)
except KeyboardInterrupt:
print("\nServer is shutting down...")
finally:
server_socket.close()
print("Server closed")
def handle_client(client_socket):
try:
while True: ## Keep receiving messages until client disconnects
## Receive data
data = client_socket.recv(1024)
## If no data, client has disconnected
if not data:
break
received_message = data.decode()
print(f"Received: {received_message}")
## Process the message (in this case, just echo it back with a prefix)
response = f"Server received: {received_message}"
client_socket.send(response.encode())
except Exception as e:
print(f"Error handling client: {e}")
finally:
## Close client socket
client_socket.close()
print("Client connection closed")
if __name__ == "__main__":
start_server()
现在,让我们创建一个交互式客户端,允许用户发送多条消息。
client.py 文件,并将代码替换为:import socket
def start_client():
## Server information
host = '127.0.0.1'
port = 12345
try:
## Create socket and connect
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(f"Connecting to server at {host}:{port}...")
client_socket.connect((host, port))
print("Connected to server!")
## Interactive message sending
while True:
## Get message from user
message = input("\nEnter message to send (or 'quit' to exit): ")
## Check if user wants to quit
if message.lower() == 'quit':
print("Closing connection...")
break
## Send message
client_socket.send(message.encode())
## Receive response
response = client_socket.recv(1024)
print(f"Response from server: {response.decode()}")
except ConnectionRefusedError:
print("Connection failed. Make sure the server is running.")
except Exception as e:
print(f"An error occurred: {e}")
finally:
## Close connection
try:
client_socket.close()
except:
pass
print("Disconnected from server")
if __name__ == "__main__":
start_client()
while True 循环以持续接受新的客户端连接handle_client 函数来管理与每个客户端的通信if not data:),这表示客户端已断开连接while True 循环以允许发送多条消息让我们测试我们增强后的服务器和客户端:
python3 ~/project/socket_lab/server.py
你应该看到:
Server running on 127.0.0.1:12345
Press Ctrl+C to stop the server
Waiting for a connection...
python3 ~/project/socket_lab/client.py
你应该看到:
Connecting to server at 127.0.0.1:12345...
Connected to server!
Enter message to send (or 'quit' to exit):
Enter message to send (or 'quit' to exit): Hello, server!
Response from server: Server received: Hello, server!
Enter message to send (or 'quit' to exit):
Connected to client: ('127.0.0.1', 59042)
Received: Hello, server!
Received: This is another message
Enter message to send (or 'quit' to exit): quit
Closing connection...
Disconnected from server
Client connection closed
Waiting for a connection...
通过这些增强,我们使用 Python sockets 创建了一个更现实和交互式的客户端 - 服务器通信系统。
在实际应用中,服务器通常需要同时处理多个客户端并妥善管理各种错误情况。让我们在考虑这些因素的情况下改进我们的实现。
有几种方法可以并发处理多个客户端:
对于这个实验,我们将实现一个基于线程的方法,它相对容易理解和实现。
让我们修改服务器以使用线程处理多个客户端:
server.py 文件,并将代码替换为:import socket
import threading
def handle_client(client_socket, client_address):
"""Handle communication with a single client"""
try:
print(f"[NEW CONNECTION] {client_address} connected.")
while True:
## Receive client data
try:
data = client_socket.recv(1024)
if not data:
break ## Client disconnected
message = data.decode()
print(f"[{client_address}] {message}")
## Send response
response = f"Message '{message}' received successfully"
client_socket.send(response.encode())
except ConnectionResetError:
print(f"[{client_address}] Connection reset by client")
break
except Exception as e:
print(f"[ERROR] {e}")
finally:
## Clean up when client disconnects
client_socket.close()
print(f"[DISCONNECTED] {client_address} disconnected")
def start_server():
"""Start the server and listen for connections"""
## Server configuration
host = '127.0.0.1'
port = 12345
## Create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## Set socket option to reuse address
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
## Bind to host and port
server_socket.bind((host, port))
## Listen for connections
server_socket.listen(5)
print(f"[STARTING] Server is listening on {host}:{port}")
while True:
## Accept client connection
client_socket, client_address = server_socket.accept()
## Create a new thread to handle the client
client_thread = threading.Thread(
target=handle_client,
args=(client_socket, client_address)
)
client_thread.daemon = True ## Thread will close when main program exits
client_thread.start()
## Display active connections
print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")
except KeyboardInterrupt:
print("\n[SHUTTING DOWN] Server is shutting down...")
except Exception as e:
print(f"[ERROR] {e}")
finally:
server_socket.close()
print("[CLOSED] Server socket closed")
if __name__ == "__main__":
start_server()
我们还应该使用更好的错误处理来增强我们的客户端:
client.py 文件,并将代码替换为:import socket
import sys
import time
def start_client():
"""Start a client that connects to the server"""
## Server information
host = '127.0.0.1'
port = 12345
## Create socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## Set a timeout for connection attempts (5 seconds)
client_socket.settimeout(5)
try:
## Connect to server
print(f"[CONNECTING] Connecting to server at {host}:{port}...")
client_socket.connect((host, port))
## Reset timeout to none for regular communication
client_socket.settimeout(None)
print("[CONNECTED] Connected to server")
## Communication loop
while True:
## Get user input
message = input("\nEnter message (or 'quit' to exit): ")
if message.lower() == 'quit':
print("[CLOSING] Closing connection by request...")
break
try:
## Send message
client_socket.send(message.encode())
## Wait for response
response = client_socket.recv(1024)
print(f"[RESPONSE] {response.decode()}")
except ConnectionResetError:
print("[ERROR] Connection was reset by the server")
break
except ConnectionAbortedError:
print("[ERROR] Connection was aborted")
break
except Exception as e:
print(f"[ERROR] {e}")
break
except socket.timeout:
print("[TIMEOUT] Connection attempt timed out. Is the server running?")
except ConnectionRefusedError:
print("[REFUSED] Connection refused. Make sure the server is running.")
except KeyboardInterrupt:
print("\n[INTERRUPT] Client shutting down...")
except Exception as e:
print(f"[ERROR] {e}")
finally:
## Close socket
try:
client_socket.close()
print("[DISCONNECTED] Disconnected from server")
except:
pass
if __name__ == "__main__":
start_client()
threading 模块以并发处理多个客户端让我们测试我们改进后的应用程序:
python3 ~/project/socket_lab/server.py
你应该看到:
[STARTING] Server is listening on 127.0.0.1:12345
python3 ~/project/socket_lab/client.py
python3 ~/project/socket_lab/client.py
[NEW CONNECTION] ('127.0.0.1', 59124) connected.
[ACTIVE CONNECTIONS] 1
[NEW CONNECTION] ('127.0.0.1', 59126) connected.
[ACTIVE CONNECTIONS] 2
[('127.0.0.1', 59124)] Hello from client 1
[('127.0.0.1', 59126)] Hello from client 2
让我们也测试一下当服务器未运行时会发生什么:
确保服务器已停止(如果正在运行,请按 Ctrl+C)
尝试运行一个客户端:
python3 ~/project/socket_lab/client.py
你应该看到:
[CONNECTING] Connecting to server at 127.0.0.1:12345...
[REFUSED] Connection refused. Make sure the server is running.
[DISCONNECTED] Disconnected from server
客户端现在可以正常处理错误,通知用户服务器可能未运行。
通过这些增强,我们创建了一个健壮的客户端 - 服务器系统,该系统可以处理多个客户端和各种错误情况。这是开发更复杂的网络应用程序的坚实基础。
祝贺你完成了这个关于 Python socket 编程的实验。你已经成功地学习了如何:
这些技能构成了网络编程的基础,可以应用于构建各种网络应用程序,从简单的聊天程序到复杂的分布式系统。
为了继续你的学习之旅,请考虑探索:
Python 的 socket 编程功能使其成为网络应用程序开发的绝佳选择,它提供了其他少数语言可以比拟的简单性和强大功能的平衡。