一个简单的客户端 服务器模型是网络编程中的基础架构,在这个模型中,服务器负责监听来自网络的连接请求,而客户端则主动发起连接到服务器,并进行数据交互,以下将分别介绍基于 Python 使用 socket
库实现该模型的具体步骤。
服务器端实现
导入必要模块
import socket
需要导入 Python 自带的 socket
模块来创建套接字对象,它是进行网络通信的基础。
创建套接字
# 创建一个 IPv4(AF_INET)、TCP(SOCK_STREAM)类型的套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
这里指定了使用 IPv4 地址族和面向连接的 TCP 协议,TCP 提供可靠的、有序的数据传输服务,适合大多数应用场景。
绑定地址和端口
host = 'localhost' # 可以使用本机回环地址,也可以设置为具体的 IP 地址或域名 port = 12345 # 选择一个未被占用的端口号 server_socket.bind((host, port))
将创建好的套接字绑定到一个特定的地址(主机名或 IP)和端口上,这样当有客户端尝试连接到这个地址和端口时,操作系统就知道应该把数据交给我们的程序处理。
开始监听连接请求
backlog = 5 # 同时能挂起的最大连接数 server_socket.listen(backlog) print(f"服务器已启动,正在监听 {host}:{port}...")
调用 listen()
方法使套接字处于被动监听状态,参数 backlog
表示允许排队等待处理的最大连接数量,服务器准备好接收客户端的连接请求。
接受客户端连接并处理数据
while True: # 阻塞等待客户端连接,返回一个新的套接字用于与该客户端通信以及客户端的信息 client_socket, client_address = server_socket.accept() print(f"收到来自 {client_address} 的连接") try: while True: # 接收客户端发送的数据,缓冲区大小设为 1024 字节 data = client_socket.recv(1024) if not data: break # 如果接收到空数据,表示客户端关闭了连接 print(f"从 {client_address} 接收到消息: {data.decode('utf-8')}") # 将接收到的数据原样返回给客户端(回声服务) client_socket.sendall(data) finally: # 确保无论如何都会关闭与客户端的连接 client_socket.close() print(f"与 {client_address} 的连接已关闭")
在一个无限循环中不断调用 accept()
方法等待新的客户端连接,一旦有客户端连接上来,就进入内层循环接收其发送的数据,使用 recv()
方法读取客户端发来的消息,并用 sendall()
方法将相同的数据回传给客户端作为响应,当检测到客户端发送了空数据(通常意味着客户端正常关闭连接),则跳出内层循环,关闭与该客户端的连接。
关闭服务器套接字(一般不会执行到这里,除非手动终止程序)
server_socket.close()
在程序结束时,记得关闭服务器端的套接字以释放资源,不过在实际运行过程中,通常是通过外部信号(如键盘中断)来终止程序运行。
客户端实现
导入必要模块
import socket
同样需要导入 socket
模块来进行网络通信相关的操作。
创建套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
创建一个新的套接字对象,用于与服务器建立连接并进行数据传输。
连接到服务器
server_host = 'localhost' # 要连接的服务器主机名或 IP 地址 server_port = 12345 # 对应的端口号必须与服务器端一致 client_socket.connect((server_host, server_port)) print(f"已成功连接到服务器 {server_host}:{server_port}")
使用 connect()
方法向指定的服务器地址和端口发起连接请求,如果连接成功,就可以开始发送和接收数据了;若失败则会抛出异常。
发送数据并接收响应
message = input("请输入要发送的消息(输入 q 退出):") while message != 'q': # 将用户输入的字符串编码为字节流后发送给服务器 client_socket.sendall(message.encode('utf-8')) # 接收服务器返回的数据并解码为字符串显示出来 response = client_socket.recv(1024) print(f"服务器回复: {response.decode('utf-8')}") message = input("请输入要发送的消息(输入 q 退出):")
用户可以在控制台输入任意文本消息,程序将其编码成字节流通过网络发送给服务器,然后等待服务器的响应,将收到的字节流解码回字符串并打印出来,当用户输入 ‘q’ 时,循环结束,程序准备退出。
关闭客户端套接字
client_socket.close()
在完成所有通信后,关闭客户端的套接字以释放系统资源。
运行示例
- 先启动服务器端程序:运行上述服务器端代码所在的脚本文件,你会看到类似如下输出表示服务器已经准备就绪:
服务器已启动,正在监听 localhost:12345...
- 再启动客户端程序:运行客户端代码所在的脚本文件,按照提示输入消息进行交互。
已成功连接到服务器 localhost:12345 请输入要发送的消息(输入 q 退出):Hello Server! 服务器回复: Hello Server! 请输入要发送的消息(输入 q 退出):How are you? 服务器回复: How are you? 请输入要发送的消息(输入 q 退出):q
- 观察服务器端日志:在服务器端可以看到相应的连接信息和数据处理记录,如:
收到来自 ('127.0.0.1', 54321) 的连接 从 ('127.0.0.1', 54321) 接收到消息: Hello Server! 从 ('127.0.0.1', 54321) 接收到消息: How are you? 与 ('127.0.0.1', 54321) 的连接已关闭
相关问题与解答
问题 1:为什么选择 TCP 而不是 UDP?
解答:TCP(传输控制协议)是一种面向连接、可靠的传输协议,它保证了数据包的顺序性和完整性,通过确认机制、重传机制等确保数据能够准确无误地到达目的地,在本示例中,我们希望实现一个简单的交互式应用,需要保证消息的顺序和可靠传输,所以选择 TCP,而 UDP(用户数据报协议)是无连接的不可靠协议,虽然传输速度快但无法保证数据的可靠性和顺序性,适用于对实时性要求较高但对数据准确性要求较低的场景,如视频直播、在线游戏等。
问题 2:如何修改代码以支持多个客户端同时连接?
解答:目前我们的服务器端代码一次只能处理一个客户端连接,要支持多个客户端同时连接,可以使用多线程或多进程技术,在使用多线程的情况下,每当有新的客户端连接时,为其创建一个新的线程来专门处理该客户端的通信任务,这样主线程可以继续监听新的连接请求,以下是简单的思路伪代码:
import threading def handle_client(client_socket, client_address): # 原来的处理单个客户端的逻辑放在这里 ... while True: client_socket, client_address = server_socket.accept() # 为每个新连接创建一个新线程进行处理 thread = threading.Thread(target=handle_client, args=(client_socket, client_address)) thread.start()
通过这种方式,服务器就能够同时处理多个客户端的连接和请求了,不过需要注意的是,多线程编程涉及到资源共享和同步等问题,需要谨慎处理
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/130123.html