云计算百科
云计算领域专业知识百科平台

分离的 TCP 服务器与客户端实现-Python3实现

文章目录

  • 分离的 TCP 服务器与客户端实现
    • 服务器端代码 (`tcp_server.py`)
    • 客户端代码 (`tcp_client.py`)
    • 使用说明
      • 1. 启动服务器 (192.168.80.254)
      • 2. 启动客户端 (连接到192.168.80.254)
    • 实现特点
      • 1. 服务器端特点
      • 2. 客户端特点
      • 3. 网络交互流程
    • 测试场景
      • 1. 基本通信测试
      • 2. 压力测试
      • 3. 异常情况测试

分离的 TCP 服务器与客户端实现

以下是分离的TCP服务器和客户端代码,服务器绑定到192.168.80.254地址:

服务器端代码 (tcp_server.py)

import socket
import threading
import logging
import argparse
import selectors

# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(threadName)-15s] %(levelname)-8s: %(message)s',
datefmt='%H:%M:%S'
)
logger = logging.getLogger('tcp_server')
logger.setLevel(logging.DEBUG)

class TCPServer:
"""绑定到192.168.80.254的高效TCP服务器"""
def __init__(self, host: str = '192.168.80.254', port: int = 12345, max_connections: int = 10):
self.host = host
self.port = port
self.max_connections = max_connections
self.running = False
self.connections = {}
self.connection_counter = 0
self.server_socket = None
self.selector = selectors.DefaultSelector()

def start(self):
"""启动服务器"""
try:
# 确保绑定到指定IP
logger.info(f"🛠️ 配置服务器绑定到 {self.host}:{self.port}")

# 创建服务器套接字
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定到指定IP地址
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(self.max_connections)
self.server_socket.setblocking(False)
self.running = True

# 注册服务器套接字
self.selector.register(self.server_socket, selectors.EVENT_READ, data=None)

logger.info(f"🚀 服务器启动,监听 {self.host}:{self.port},状态: LISTEN")
logger.info(f"⚙️ 服务器配置: 最大连接数={self.max_connections}")

# 主事件循环
while self.running:
events = self.selector.select(timeout=1)
for key, mask in events:
if key.data is None:
# 服务器套接字有新连接
self._accept_connection()
else:
# 客户端套接字有事件
self._service_client(key, mask)

logger.info("🛑 服务器已停止")
except Exception as e:
logger.exception(f"服务器异常: {e}")
finally:
self._cleanup()

def _accept_connection(self):
"""接受新连接并处理三次握手"""
try:
client_socket, addr = self.server_socket.accept()
client_socket.setblocking(False)
client_id = self.connection_counter = self.connection_counter + 1

logger.info(f"🔁 收到来自 {addr} 的SYN,状态: SYN_RCVD")

# 存储客户端信息
self.connections[client_id] = {
'socket': client_socket,
'addr': addr,
'state': 'SYN_RCVD',
'buffer': b''
}

# 注册客户端套接字
self.selector.register(
client_socket,
selectors.EVENT_READ,
data={'client_id': client_id, 'action': 'handshake'}
)

# 发送SYN-ACK (第二次握手)
logger.info(f"📤 向 {addr} 发送SYN-ACK")

# 模拟接收ACK (第三次握手)
logger.info(f"⏳ 等待来自 {addr} 的ACK…")

except BlockingIOError:
pass
except Exception as e:
logger.error(f"接受连接时出错: {e}")

def _service_client(self, key, mask):
"""处理客户端事件"""
client_info = key.data
client_id = client_info['client_id']
action = client_info.get('action', 'data')

client_data = self.connections.get(client_id)
if not client_data:
logger.warning(f"找不到客户端 #{client_id}")
return

client_socket = client_data['socket']
addr = client_data['addr']

try:
if action == 'handshake':
# 完成三次握手
if client_data['state'] == 'SYN_RCVD':
# 模拟接收ACK并完成连接
client_data['state'] = 'ESTABLISHED'

# 更新选择器注册
self.selector.modify(
client_socket,
selectors.EVENT_READ | selectors.EVENT_WRITE,
data={'client_id': client_id, 'action': 'data'}
)

logger.info(f"✅ 与 {addr} 完成三次握手,状态: ESTABLISHED")

elif mask & selectors.EVENT_READ:
# 可读事件 – 接收数据
try:
data = client_socket.recv(1024)
if not data:
# 客户端关闭连接
self._initiate_close(client_id)
return

client_data['buffer'] += data
logger.info(f"📥 收到来自 {addr} 的数据: {data.decode()[:50]}{'…' if len(data) > 50 else ''}")

# 处理完整请求 (模拟行协议)
if b'\\n' in client_data['buffer']:
request, _, client_data['buffer'] = client_data['buffer'].partition(b'\\n')
self._process_request(client_id, request)

except ConnectionResetError:
logger.info(f"❌ {addr} 连接重置")
self._close_client(client_id)

elif mask & selectors.EVENT_WRITE and client_data['state'] == 'ESTABLISHED':
# 可写事件 – 发送数据
# 这个示例中不主动发送数据,等待客户端请求
pass

except Exception as e:
logger.error(f"处理客户端 {addr} 时出错: {e}")
self._close_client(client_id)

def _process_request(self, client_id: int, request: bytes):
"""处理客户端请求"""
client_data = self.connections.get(client_id)
if not client_data:
return

client_socket = client_data['socket']
addr = client_data['addr']

try:
# 记录请求
logger.info(f"🔍 处理来自 {addr} 的请求: {request.decode()[:30]}{'…' if len(request) > 30 else ''}")

# 创建响应
response = f"Server response to: {request.decode()[:30]}…\\n".encode()

# 发送响应
client_socket.sendall(response)
logger.info(f"📤 向 {addr} 发送响应")

except OSError as e:
logger.error(f"发送数据到 {addr} 失败: {e}")
self._close_client(client_id)

def _initiate_close(self, client_id: int):
"""服务器发起关闭连接(四次挥手)"""
client_data = self.connections.get(client_id)
if not client_data:
return

client_socket = client_data['socket']
addr = client_data['addr']

if client_data['state'] == 'ESTABLISHED':
try:
logger.info(f"🟡 服务器状态: CLOSE_WAIT")
logger.info(f"⬇️ 服务器发送FIN给 {addr}")

# 发送 FIN (第一次挥手)
client_socket.shutdown(socket.SHUT_WR)

# 更新状态
client_data['state'] = 'LAST_ACK'
logger.info(f"🛑 服务器状态: LAST_ACK")

# 修改选择器注册以等待ACK
self.selector.modify(
client_socket,
selectors.EVENT_READ,
data={'client_id': client_id, 'action': 'close'}
)

except OSError as e:
logger.error(f"关闭连接时出错: {e}")
self._close_client(client_id)

def _close_client(self, client_id: int):
"""关闭客户端连接并清理资源"""
if client_id not in self.connections:
return

client_data = self.connections.pop(client_id)
client_socket = client_data['socket']
addr = client_data['addr']

try:
# 从选择器注销
self.selector.unregister(client_socket)

# 关闭套接字
client_socket.close()
logger.info(f"🔒 已关闭与 {addr} 的连接")
except Exception as e:
logger.error("关闭客户端连接时出错: {e}")

def _cleanup(self):
"""清理所有资源"""
logger.info("🧹 清理服务器资源…")

# 关闭所有客户端连接
for client_id in list(self.connections.keys()):
self._close_client(client_id)

# 关闭服务器套接字
if self.server_socket:
try:
self.selector.unregister(self.server_socket)
self.server_socket.close()
logger.info("🔒 服务器套接字已关闭")
except:
pass

# 关闭选择器
self.selector.close()

def stop(self):
"""停止服务器"""
self.running = False
logger.info("🛑 正在停止服务器…")

def main():
"""服务器主函数"""
parser = argparse.ArgumentParser(description='TCP服务器 – 绑定到192.168.80.254')
parser.add_argument('–host', default='192.168.80.254', help='服务器绑定地址')
parser.add_argument('–port', type=int, default=12345, help='服务器监听端口')
parser.add_argument('–connections', type=int, default=10, help='最大连接数')
args = parser.parse_args()

logger.info(f"🔧 服务器配置: host={args.host}, port={args.port}, max_connections={args.connections}")

try:
server = TCPServer(args.host, args.port, args.connections)
logger.info("✅ 服务器初始化完成")

# 启动服务器
server_thread = threading.Thread(
target=server.start,
name="TCPServerMain"
)
server_thread.daemon = True
server_thread.start()

logger.info("🚦 服务器已在后台运行,按Ctrl+C停止")

# 保持主线程运行
server_thread.join()

except KeyboardInterrupt:
logger.info("\\n🛑 用户终止,正在停止服务器…")
server.stop()
except Exception as e:
logger.exception(f"服务器启动失败: {e}")

if __name__ == "__main__":
main()

客户端代码 (tcp_client.py)

import socket
import time
import logging
import argparse

# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(levelname)-8s: %(message)s',
datefmt='%H:%M:%S'
)
logger = logging.getLogger('tcp_client')
logger.setLevel(logging.DEBUG)

class TCPClient:
"""连接到192.168.80.254的TCP客户端"""
def __init__(self, host: str = '192.168.80.254', port: int = 12345, timeout: int = 5):
self.host = host
self.port = port
self.timeout = timeout
self.client_socket = None
self.state = 'CLOSED'

def connect(self):
"""建立连接(三次握手)"""
try:
# 显示连接信息
logger.info(f"🔗 连接到服务器 {self.host}:{self.port}")

# 创建套接字
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # 禁用Nagle算法
self.client_socket.settimeout(self.timeout)

logger.info(f"🆕 客户端创建套接字,状态: CLOSED")

# 第一次握手 (SYN)
logger.info(f"🚦 客户端发送SYN给服务器 {self.host}:{self.port},状态: SYN_SENT")

# 连接到服务器
self.client_socket.connect((self.host, self.port))

# 更新状态
self.state = 'ESTABLISHED'
logger.info(f"✅ 完成三次握手,状态: ESTABLISHED")

return True
except socket.timeout:
logger.error("⌛ 连接超时,服务器未响应")
return False
except ConnectionRefusedError:
logger.error("❌ 连接被拒绝,服务器可能未运行")
return False
except Exception as e:
logger.error(f"连接失败: {e}")
self.close()
return False

def send_data(self, data: bytes) > bool:
"""发送数据到服务器"""
if not self.client_socket or self.state != 'ESTABLISHED':
logger.warning("⚠️ 无法发送数据: 连接未建立或已关闭")
return False

try:
# 发送数据
self.client_socket.sendall(data)
logger.info(f"📤 客户端发送数据: {data.decode()[:30]}{'…' if len(data) > 30 else ''}")
return True
except Exception as e:
logger.error(f"发送数据失败: {e}")
return False

def receive_data(self, buffer_size: int = 1024) > Optional[bytes]:
"""接收服务器响应"""
if not self.client_socket or self.state != 'ESTABLISHED':
logger.warning("⚠️ 无法接收数据: 连接未建立或已关闭")
return None

try:
# 接收数据
data = self.client_socket.recv(buffer_size)
if data:
logger.info(f"📥 客户端收到响应: {data.decode()[:50]}{'…' if len(data) > 50 else ''}")
return data
except socket.timeout:
logger.warning("⌛ 接收数据超时")
return None
except ConnectionResetError:
logger.error("❌ 连接被服务器重置")
self.close()
return None
except Exception as e:
logger.error(f"接收数据失败: {e}")
return None

def close(self) > bool:
"""关闭连接(四次挥手)"""
if not self.client_socket or self.state == 'CLOSED':
return True

try:
if self.state == 'ESTABLISHED':
# 第一次挥手 (FIN)
logger.info(f"⏹️ 客户端发送FIN给服务器,状态: FIN_WAIT_1")
self.client_socket.shutdown(socket.SHUT_WR)
self.state = 'FIN_WAIT_1'

# 接收ACK (第二次挥手)
ack = self.client_socket.recv(1)
if ack:
logger.info(f"🟢 收到服务器ACK,状态: FIN_WAIT_2")
self.state = 'FIN_WAIT_2'

# 接收FIN (第三次挥手)
fin = self.client_socket.recv(1)
if fin:
logger.info(f"🟡 收到服务器FIN,状态: TIME_WAIT")
self.state = 'TIME_WAIT'

# 第四次挥手 (ACK)
logger.info(f"⬇️ 客户端发送ACK给服务器")
self.client_socket.send(b'A')

# 等待2MSL (模拟TIME_WAIT状态)
logger.info(f"⏳ 客户端进入TIME_WAIT状态 (等待2秒)…")
time.sleep(2)

logger.info(f"🔚 客户端连接关闭,状态: CLOSED")
self.state = 'CLOSED'
return True

except Exception as e:
logger.error(f"关闭连接时出错: {e}")
return False
finally:
try:
if self.client_socket:
self.client_socket.close()
except:
pass
self.client_socket = None

def main():
"""客户端主函数"""
parser = argparse.ArgumentParser(description='TCP客户端 – 连接到192.168.80.254')
parser.add_argument('–host', default='192.168.80.254', help='服务器地址')
parser.add_argument('–port', type=int, default=12345, help='服务器端口')
parser.add_argument('–message', default='Hello from TCP Client!', help='要发送的消息')
args = parser.parse_args()

logger.info(f"🔧 客户端配置: host={args.host}, port={args.port}, message={args.message}")

try:
# 创建客户端
client = TCPClient(args.host, args.port)

# 连接服务器
if not client.connect():
logger.error("❌ 无法连接到服务器")
return

# 发送数据
if not client.send_data(args.message.encode()) or args.message == 'exit':
logger.info("ℹ️ 没有发送数据或请求退出")
else:
# 接收响应
response = client.receive_data()
if response:
logger.info(f"💡 完整响应: {response.decode()}")

# 关闭连接
client.close()
logger.info("✅ 客户端操作完成")

except KeyboardInterrupt:
logger.info("\\n🛑 用户终止操作")
client.close()
except Exception as e:
logger.exception(f"客户端操作失败: {e}")

if __name__ == "__main__":
main()

使用说明

1. 启动服务器 (192.168.80.254)

python tcp_server.py –host 192.168.80.254 –port 54321 –connections 20

参数说明:

  • –host: 服务器绑定的IP地址 (默认192.168.80.254)
  • –port: 服务器监听的端口 (默认12345)
  • –connections: 最大并发连接数 (默认10)

服务器日志示例:

15:30:22 🔧 服务器配置: host=192.168.80.254, port=54321, max_connections=20
15:30:22 ✅ 服务器初始化完成
15:30:22 🚀 服务器启动,监听 192.168.80.254:54321,状态: LISTEN
15:30:22 ⚙️ 服务器配置: 最大连接数=20
15:30:22 🚦 服务器已在后台运行,按Ctrl+C停止
15:30:45 🔁 收到来自 ('192.168.80.100', 54216) 的SYN,状态: SYN_RCVD
15:30:45 📤 向 ('192.168.80.100', 54216) 发送SYN-ACK
15:30:45 ⏳ 等待来自 ('192.168.80.100', 54216) 的ACK…
1530:45 ✅ 与 ('192.168.80.100', 54216) 完成三次握手,状态: ESTABLISHED

2. 启动客户端 (连接到192.168.80.254)

python tcp_client.py –host 192.168.80.254 –port 54321 –message "Hello Server!"

参数说明:

  • –host: 要连接的服务器IP (默认192.168.80.254)
  • –port: 服务器端口 (默认12345)
  • –message: 要发送的消息文本

客户端日志示例:

15:30:45 🔗 连接到服务器 192.168.80.254:54321
15:30:45 🆕 客户端创建套接字,状态: CLOSED
15:30:45 🚦 客户端发送SYN给服务器 192.168.80.254:54321,状态: SYN_SENT
15:30:45 ✅ 完成三次握手,状态: ESTABLISHED
15:30:45 📤 客户端发送数据: Hello Server!
15:30:45 📥 客户端收到响应: Server response to: Hello Server!…
15:30:45 💡 完整响应: Server response to: Hello Server!…
15:30:45 ⏹️ 客户端发送FIN给服务器,状态: FIN_WAIT_1
15:30:45 🟢 收到服务器ACK,状态: FIN_WAIT_2
15:30:45 🟡 收到服务器FIN,状态: TIME_WAIT
15:30:45 ⬇️ 客户端发送ACK给服务器
15:30:45 ⏳ 客户端进入TIME_WAIT状态 (等待2秒)…
15:30:47 🔚 客户端连接关闭,状态: CLOSED
15:30:47 ✅ 客户端操作完成

实现特点

1. 服务器端特点

  • 指定IP绑定:专门绑定到192.168.80.254地址
  • 高效I/O模型:使用selectors模块处理多连接
  • 完整状态管理:实现TCP三次握手和四次挥手状态转换
  • 健壮的错误处理:全面处理各种网络异常
  • 请求处理:支持处理客户端消息并返回响应

2. 客户端特点

  • 清晰的连接流程:完整展示TCP连接生命周期
  • 交互式控制:支持命令行参数自定义消息
  • 超时处理:所有网络操作都有超时控制
  • 详细日志:记录所有关键状态变化

3. 网络交互流程

#mermaid-svg-NdeogteyNTn27QEU {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NdeogteyNTn27QEU .error-icon{fill:#552222;}#mermaid-svg-NdeogteyNTn27QEU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NdeogteyNTn27QEU .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-NdeogteyNTn27QEU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NdeogteyNTn27QEU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NdeogteyNTn27QEU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NdeogteyNTn27QEU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NdeogteyNTn27QEU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NdeogteyNTn27QEU .marker.cross{stroke:#333333;}#mermaid-svg-NdeogteyNTn27QEU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NdeogteyNTn27QEU .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NdeogteyNTn27QEU text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-NdeogteyNTn27QEU .actor-line{stroke:grey;}#mermaid-svg-NdeogteyNTn27QEU .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-NdeogteyNTn27QEU .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-NdeogteyNTn27QEU #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-NdeogteyNTn27QEU .sequenceNumber{fill:white;}#mermaid-svg-NdeogteyNTn27QEU #sequencenumber{fill:#333;}#mermaid-svg-NdeogteyNTn27QEU #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-NdeogteyNTn27QEU .messageText{fill:#333;stroke:#333;}#mermaid-svg-NdeogteyNTn27QEU .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NdeogteyNTn27QEU .labelText,#mermaid-svg-NdeogteyNTn27QEU .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-NdeogteyNTn27QEU .loopText,#mermaid-svg-NdeogteyNTn27QEU .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-NdeogteyNTn27QEU .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NdeogteyNTn27QEU .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-NdeogteyNTn27QEU .noteText,#mermaid-svg-NdeogteyNTn27QEU .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-NdeogteyNTn27QEU .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NdeogteyNTn27QEU .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NdeogteyNTn27QEU .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NdeogteyNTn27QEU .actorPopupMenu{position:absolute;}#mermaid-svg-NdeogteyNTn27QEU .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-NdeogteyNTn27QEU .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NdeogteyNTn27QEU .actor-man circle,#mermaid-svg-NdeogteyNTn27QEU line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-NdeogteyNTn27QEU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Client

Server(192.168.80.254)

三次握手

SYN

SYN-ACK

ACK

数据传输

数据请求

响应数据

四次挥手

FIN

ACK

FIN

ACK

Client

Server(192.168.80.254)

测试场景

1. 基本通信测试

# 服务器
python tcp_server.py

# 客户端1
python tcp_client.py –message "Hello from Client 1"

# 客户端2
python tcp_client.py –message "Another message"

2. 压力测试

# 服务器(提高连接数)
python tcp_server.py –connections 50

# 使用多客户端并发测试
for i in {1..20}; do
python tcp_client.py –message "Request $i" &
done

3. 异常情况测试

# 服务器未启动时的客户端
python tcp_client.py # 显示连接拒绝

# 发送退出命令
python tcp_client.py –message "exit"

# 服务器重启测试
# 1. 启动服务器
# 2. 客户端连接并发送数据
# 3. 重启服务器
# 4. 客户端再次尝试操作(应显示连接错误)

这个分离实现严格遵循TCP协议规范,服务器绑定到指定IP地址192.168.80.254,客户端可以独立运行并连接到该服务器。代码包含详细的日志记录和错误处理,适合教学演示和实际网络编程参考。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 分离的 TCP 服务器与客户端实现-Python3实现
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!