网络编程入门:Socket、TCP/UDP与客户端/服务器模型
网络编程入门:Socket、TCP/UDP与客户端/服务器模型
网络编程是现代软件开发的核心技能之一。Python内置的socket模块提供了底层网络通信能力,让你能够构建客户端和服务器应用。本文将带你从零开始掌握网络编程基础。
什么是Socket
Socket(套接字)是网络通信的端点,是应用程序与网络协议之间的接口。通过Socket,不同主机上的应用程序可以互相发送和接收数据。Python的socket模块封装了底层的网络操作,提供了简洁的API。
import socket
# 查看本机主机名
hostname = socket.gethostname()
print(f"主机名: {hostname}")
# 获取本机IP地址
ip_address = socket.gethostbyname(hostname)
print(f"IP地址: {ip_address}")
TCP与UDP的区别
TCP(传输控制协议)和UDP(用户数据报协议)是两种最主要的传输层协议:
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接,三次握手 | 无连接 |
| 可靠性 | 可靠传输,保证顺序 | 不保证可靠 |
| 速度 | 较慢 | 较快 |
| 适用场景 | 网页、文件传输、邮件 | 视频流、游戏、DNS查询 |
TCP客户端编程
TCP客户端需要先连接到服务器,然后才能发送和接收数据:
import socket
def tcp_client():
# 创建TCP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# 连接服务器
host = "www.example.com"
port = 80
client_socket.connect((host, port))
print(f"已连接到 {host}:{port}")
# 发送HTTP请求
request = f"GET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n"
client_socket.send(request.encode())
# 接收响应
response = b""
while True:
data = client_socket.recv(1024)
if not data:
break
response += data
print(f"收到响应,长度: {len(response)} 字节")
print(response.decode()[:200])
finally:
client_socket.close()
tcp_client()
TCP服务器编程
TCP服务器需要监听端口,等待客户端连接,然后处理请求:
import socket
import threading
def handle_client(client_socket, address):
"""处理客户端连接"""
print(f"新连接来自: {address}")
try:
while True:
# 接收数据
data = client_socket.recv(1024)
if not data:
break
message = data.decode()
print(f"收到消息: {message}")
# 回显消息(大写)
response = message.upper()
client_socket.send(response.encode())
except ConnectionResetError:
print(f"客户端 {address} 强制断开")
finally:
client_socket.close()
print(f"连接关闭: {address}")
def tcp_server():
# 创建服务器套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置SO_REUSEADDR,避免"地址已被使用"错误
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址和端口
host = "127.0.0.1"
port = 8888
server_socket.bind((host, port))
# 开始监听
server_socket.listen(5)
print(f"服务器监听 {host}:{port}")
try:
while True:
# 接受新连接
client_socket, address = server_socket.accept()
# 为每个客户端创建线程
thread = threading.Thread(
target=handle_client,
args=(client_socket, address)
)
thread.daemon = True
thread.start()
except KeyboardInterrupt:
print("\n服务器关闭")
finally:
server_socket.close()
tcp_server()
UDP编程
UDP是无连接的,不需要建立连接就可以直接发送数据:
import socket
def udp_server():
"""UDP服务器"""
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
host = "127.0.0.1"
port = 9999
server_socket.bind((host, port))
print(f"UDP服务器监听 {host}:{port}")
while True:
# 接收数据(同时获取客户端地址)
data, address = server_socket.recvfrom(1024)
message = data.decode()
print(f"收到来自 {address} 的消息: {message}")
# 回复客户端
response = f"服务器收到: {message}"
server_socket.sendto(response.encode(), address)
def udp_client():
"""UDP客户端"""
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ("127.0.0.1", 9999)
# 发送消息
message = "你好,UDP服务器!"
client_socket.sendto(message.encode(), server_address)
# 接收回复
data, address = client_socket.recvfrom(1024)
print(f"收到回复: {data.decode()}")
client_socket.close()
非阻塞Socket
默认情况下Socket是阻塞的,可以设置为非阻塞模式:
import socket
import select
def non_blocking_example():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("127.0.0.1", 7777))
server_socket.listen(5)
# 设置为非阻塞
server_socket.setblocking(False)
# 使用select进行I/O多路复用
readable = [server_socket]
writable = []
exceptional = []
while True:
# 等待事件,超时1秒
readable, writable, exceptional = select.select(
readable, writable, exceptional, 1.0
)
for s in readable:
if s is server_socket:
# 新连接
client, address = s.accept()
print(f"新连接: {address}")
readable.append(client)
else:
# 客户端数据
try:
data = s.recv(1024)
if data:
print(f"收到数据: {data.decode()}")
s.send(data.upper())
else:
s.close()
readable.remove(s)
except ConnectionResetError:
s.close()
readable.remove(s)
实战示例:简单聊天室
import socket
import threading
import json
class ChatServer:
def __init__(self, host="127.0.0.1", port=8888):
self.host = host
self.port = port
self.clients = {}
self.server_socket = None
def start(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(10)
print(f"聊天室服务器启动: {self.host}:{self.port}")
while True:
client_socket, address = self.server_socket.accept()
threading.Thread(
target=self.handle_client,
args=(client_socket, address),
daemon=True
).start()
def handle_client(self, client_socket, address):
try:
# 询问昵称
client_socket.send("请输入你的昵称: ".encode())
nickname = client_socket.recv(1024).decode().strip()
self.clients[client_socket] = nickname
self.broadcast(f"[系统] {nickname} 加入了聊天室", client_socket)
print(f"{nickname} ({address}) 已连接")
while True:
data = client_socket.recv(4096)
if not data:
break
message = data.decode().strip()
if message.lower() == "quit":
break
self.broadcast(f"{nickname}: {message}", client_socket)
except (ConnectionResetError, OSError):
pass
finally:
nickname = self.clients.pop(client_socket, "未知用户")
client_socket.close()
self.broadcast(f"[系统] {nickname} 离开了聊天室")
print(f"{nickname} 已断开")
def broadcast(self, message, exclude=None):
for sock in list(self.clients.keys()):
if sock != exclude:
try:
sock.send(message.encode())
except OSError:
pass
if __name__ == "__main__":
server = ChatServer()
server.start()
总结
网络编程是构建分布式应用的基础。掌握Socket编程后,你可以进一步学习HTTP框架、WebSocket等高级网络技术。在实际开发中,通常使用现成的框架(如Flask、FastAPI)来处理网络通信,但理解底层原理对于调试和优化至关重要。