一、核心基础概念
1. 网络服务级别
- 低级别的网络服务:支持标准 BSD Sockets API,可访问底层操作系统 Socket 接口全部方法。
- 高级别的网络服务:通过
SocketServer 模块提供服务器中心类,简化网络服务器开发。
2. Socket 定义
- 又称 "套接字",是应用程序与网络交互的接口,用于发起网络请求或应答请求,实现主机间 / 同一计算机进程间的通讯。
二、socket () 函数(创建套接字)
1. 语法格式
复制代码
socket.socket([family[, type[, proto]]])
2. 参数说明
| 参数 |
可选值 |
功能描述 |
| family |
AF_UNIX、AF_INET |
指定套接字家族,AF_INET 用于网络通讯(常用),AF_UNIX 用于本地进程间通讯 |
| type |
SOCK_STREAM、SOCK_DGRAM |
套接字类型:SOCK_STREAM(TCP 面向连接,可靠传输)、SOCK_DGRAM(UDP 无连接,快速传输) |
| proto |
0(默认) |
协议号,一般无需手动指定 |
三、Socket 核心方法(按功能分类)
1. 服务器端专用方法
| 方法 |
功能描述 |
| s.bind((host, port)) |
绑定地址到套接字,AF_INET 下地址格式为(主机名 / IP,端口号)元组 |
| s.listen(backlog) |
启动 TCP 监听,backlog 为拒绝连接前可挂起的最大连接数(至少为 1,常用 5) |
| s.accept() |
被动接受 TCP 客户端连接(阻塞式),返回(clientsocket 客户端套接字,addr 客户端地址)元组 |
2. 客户端专用方法
| 方法 |
功能描述 |
| s.connect((host, port)) |
主动初始化 TCP 连接,地址格式为(主机名 / IP,端口号)元组,连接失败抛 socket.error |
| s.connect_ex((host, port)) |
connect () 扩展版,连接失败返回出错码而非抛出异常 |
3. 公共通用方法
| 方法 |
功能描述 |
| s.recv(bufsize) |
接收 TCP 数据,返回字符串形式数据,bufsize 为最大接收字节数 |
| s.send(string) |
发送 TCP 数据,返回实际发送字节数(可能小于字符串字节数) |
| s.sendall(string) |
完整发送 TCP 数据,成功返回 None,失败抛异常 |
| s.recvfrom(bufsize) |
接收 UDP 数据,返回(data 接收数据,address 发送方地址)元组 |
| s.sendto(data, (ipaddr, port)) |
发送 UDP 数据,address 为目标地址元组,返回发送字节数 |
| s.close() |
关闭套接字 |
| s.getpeername() |
返回连接套接字的远程地址(ipaddr, port)元组 |
| s.getsockname() |
返回套接字自身地址(ipaddr, port)元组 |
| s.setsockopt(level, optname, value) |
设置套接字选项值 |
| s.getsockopt(level, optname[.buflen]) |
获取套接字选项值 |
| s.settimeout(timeout) |
设置操作超时期(秒为单位,None 表示无超时),建议创建时设置 |
| s.gettimeout() |
返回当前超时期值(None 表示无超时) |
| s.setblocking(flag) |
设置阻塞模式:True 为阻塞(默认),False 为非阻塞(无数据时抛 socket.error) |
四、实战实例
1. 简单 TCP 服务器 - 客户端(单向通讯)
- 导入 socket、sys 模块,创建 TCP 套接字(AF_INET + SOCK_STREAM)。
- 获取本地主机名,绑定端口(例:9999),设置最大连接数 5。
- 循环监听客户端连接,接受连接后发送欢迎语,关闭客户端套接字。
- 创建 TCP 套接字,连接服务器(主机名 + 端口 9999)。
- 接收服务器数据(最大 1024 字节),解码后打印,关闭套接字。
2. 带时间戳的 TCP 双向通讯
服务器端
- 核心增强:设置端口重用,循环接收客户端消息,拼接时间戳后返回给客户端,捕获断开异常。
- 关键逻辑:
msg = time.strftime("%Y-%m-%d %X") + ":" + 客户端消息。
客户端
- 核心逻辑:循环输入消息发送给服务器,接收带时间戳的响应并打印,输入为空则退出。
3. 多人聊天室(支持群聊 + 私聊,多线程实现)
服务器端核心设计
- 数据结构:维护客户端地址 - 名称映射、名称 - 客户端映射、所有客户端列表。
- 多线程:每接入一个客户端启动一个线程,独立处理该客户端消息。
- 消息机制:
- 群聊:接收消息后向所有客户端广播。
- 私聊:消息以
@用户名 内容 格式,提取目标用户并定向发送。
- 状态管理:客户端断开时,移除该客户端,向所有用户广播退出通知。
客户端核心设计
- 连接服务器后发送用户名,启动独立线程监听并打印服务器消息。
- 主线程循环输入消息并发送,支持群聊(直接输入)和私聊(@格式输入)。
4. 代码实现
简单 TCP 服务器 - 客户端(单向通讯)
python
复制代码
# 导入所需模块
import socket
import sys
# 1. 创建 TCP 套接字(AF_INET:网络通讯,SOCK_STREAM:TCP协议)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 获取本地主机地址(0.0.0.0 允许所有IP访问),绑定端口9999
host = '0.0.0.0'
port = 9999
server_socket.bind((host, port))
# 3. 设置监听,最大挂起连接数为5
server_socket.listen(5)
print(f"服务器启动,监听 {host}:{port},等待客户端连接...")
# 4. 循环接收客户端连接(服务端常驻)
while True:
# 接受客户端连接,返回(客户端套接字,客户端地址)
client_socket, client_addr = server_socket.accept()
print(f"客户端 {client_addr} 已连接")
# 5. 发送欢迎语(需编码为字节流)
welcome_msg = "欢迎连接TCP服务器!(单向通讯)"
client_socket.send(welcome_msg.encode('utf-8'))
# 6. 关闭当前客户端套接字(单向通讯,发完消息即关闭)
client_socket.close()
print(f"客户端 {client_addr} 连接已关闭\n")
python
复制代码
import socket
import sys
# 1. 创建 TCP 套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器(替换为实际服务器IP,本地测试用127.0.0.1)
server_host = '127.0.0.1'
server_port = 9999
try:
client_socket.connect((server_host, server_port))
print(f"成功连接服务器 {server_host}:{server_port}")
# 3. 接收服务器数据(最大1024字节),解码为字符串
recv_data = client_socket.recv(1024).decode('utf-8')
print("收到服务器消息:", recv_data)
except Exception as e:
print("连接失败:", e)
finally:
# 4. 关闭套接字
client_socket.close()
print("客户端已关闭")
带时间戳的 TCP 双向通讯
python
复制代码
import socket
import sys
import time
# 1. 创建 TCP 套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 设置端口重用(避免程序重启后端口占用)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定地址和端口
host = '0.0.0.0'
port = 9999
server_socket.bind((host, port))
server_socket.listen(5)
print(f"双向通讯服务器启动,监听 {host}:{port}...")
while True:
client_socket, client_addr = server_socket.accept()
print(f"\n客户端 {client_addr} 已连接")
# 设置客户端套接字超时(避免阻塞)
client_socket.settimeout(30)
try:
# 循环接收客户端消息
while True:
# 接收客户端消息
recv_data = client_socket.recv(1024).decode('utf-8')
if not recv_data: # 客户端断开时,recv返回空
break
print(f"收到 {client_addr} 消息:{recv_data}")
# 拼接时间戳并返回
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
send_msg = f"[{timestamp}] 服务器回复:{recv_data}"
client_socket.send(send_msg.encode('utf-8'))
except socket.timeout:
print(f"客户端 {client_addr} 超时未发送消息")
except Exception as e:
print(f"与 {client_addr} 通讯异常:{e}")
finally:
client_socket.close()
print(f"客户端 {client_addr} 连接关闭")
python
复制代码
import socket
import sys
# 1. 创建 TCP 套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器
server_host = '127.0.0.1'
server_port = 9999
try:
client_socket.connect((server_host, server_port))
print(f"连接服务器 {server_host}:{server_port} 成功")
print("输入消息发送给服务器(输入空行退出):")
# 3. 循环输入并发送消息
while True:
send_msg = input("> ")
if not send_msg: # 输入为空则退出
break
# 发送消息(编码为字节流)
client_socket.send(send_msg.encode('utf-8'))
# 接收服务器带时间戳的回复
recv_data = client_socket.recv(1024).decode('utf-8')
print("服务器回复:", recv_data)
except Exception as e:
print("通讯异常:", e)
finally:
client_socket.close()
print("客户端已退出")
多人聊天室(支持群聊 + 私聊,多线程实现)
1. 服务器端(server_chat.py)
python
复制代码
import socket
import threading
import time
# 全局变量:维护客户端映射(线程安全,简化版未加锁,实际需加互斥锁)
# 用户名: 客户端套接字
clients = {}
# 客户端地址: 用户名
addr2name = {}
# 处理单个客户端消息的线程函数
def handle_client(client_socket, client_addr):
print(f"新线程启动:处理 {client_addr} 消息")
try:
# 1. 接收客户端用户名
username = client_socket.recv(1024).decode('utf-8').strip()
if not username or username in clients:
client_socket.send("用户名不能为空/已存在,请重新输入!".encode('utf-8'))
client_socket.close()
return
# 2. 记录客户端信息
clients[username] = client_socket
addr2name[client_addr] = username
print(f"{username}({client_addr})加入聊天室")
# 3. 广播新人加入消息
broadcast_msg = f"【系统】{username} 加入聊天室!当前在线:{list(clients.keys())}"
broadcast(broadcast_msg, exclude=None)
# 4. 循环接收该客户端消息
while True:
msg = client_socket.recv(1024).decode('utf-8')
if not msg: # 客户端断开
break
# 5. 判断私聊/群聊
if msg.startswith('@'):
# 私聊格式:@用户名 消息内容
parts = msg.split(' ', 1)
if len(parts) < 2:
client_socket.send("私聊格式错误!正确:@用户名 消息".encode('utf-8'))
continue
target_name = parts[0][1:] # 提取目标用户名
target_socket = clients.get(target_name)
if target_socket:
private_msg = f"【{username} 私聊】{parts[1]}"
target_socket.send(private_msg.encode('utf-8'))
# 给发送方确认
client_socket.send(f"【私聊成功】发送给 {target_name}:{parts[1]}".encode('utf-8'))
else:
client_socket.send(f"【私聊失败】用户 {target_name} 不在线!".encode('utf-8'))
else:
# 群聊:广播消息
group_msg = f"【{username}】{msg}"
broadcast(group_msg, exclude=client_socket)
except Exception as e:
print(f"{addr2name.get(client_addr, '未知用户')}({client_addr})异常:{e}")
finally:
# 6. 客户端断开,清理数据并广播
if client_addr in addr2name:
username = addr2name.pop(client_addr)
if username in clients:
del clients[username]
leave_msg = f"【系统】{username} 离开聊天室!当前在线:{list(clients.keys())}"
broadcast(leave_msg, exclude=None)
print(f"{username}({client_addr})离开聊天室")
client_socket.close()
# 广播消息函数(exclude:排除的客户端,None则广播所有人)
def broadcast(msg, exclude):
for sock in clients.values():
if sock != exclude:
try:
sock.send(msg.encode('utf-8'))
except:
# 发送失败则移除该客户端
sock.close()
clients.pop(list(clients.keys())[list(clients.values()).index(sock)])
# 主函数:启动聊天室服务器
def main():
# 创建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址和端口
host = '0.0.0.0'
port = 9999
server_socket.bind((host, port))
server_socket.listen(10) # 最大10个客户端连接
print(f"聊天室服务器启动,监听 {host}:{port},等待用户加入...")
# 循环接收客户端连接,每个客户端启动独立线程
while True:
client_socket, client_addr = server_socket.accept()
# 启动线程处理该客户端
client_thread = threading.Thread(target=handle_client, args=(client_socket, client_addr))
client_thread.daemon = True # 主线程退出时,子线程也退出
client_thread.start()
if __name__ == "__main__":
main()
2. 客户端(client_chat.py)
python
复制代码
import socket
import threading
import sys
# 接收服务器消息的线程函数
def recv_msg(client_socket):
while True:
try:
# 持续接收服务器消息
msg = client_socket.recv(1024).decode('utf-8')
if not msg:
break
print("\n" + msg) # 换行避免覆盖输入框
print("> ", end="", flush=True) # 重新显示输入提示符
except Exception as e:
print(f"\n接收消息异常:{e}")
break
# 接收失败/断开,关闭客户端
client_socket.close()
sys.exit(0)
# 主函数:启动聊天室客户端
def main():
# 1. 创建TCP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器
server_host = '127.0.0.1'
server_port = 9999
try:
client_socket.connect((server_host, server_port))
print(f"成功连接聊天室服务器 {server_host}:{server_port}")
except Exception as e:
print(f"连接服务器失败:{e}")
return
# 3. 输入并发送用户名
while True:
username = input("请输入你的用户名:").strip()
if username:
client_socket.send(username.encode('utf-8'))
break
print("用户名不能为空!")
# 4. 启动接收消息的线程
recv_thread = threading.Thread(target=recv_msg, args=(client_socket,))
recv_thread.daemon = True
recv_thread.start()
# 5. 主线程循环输入并发送消息
print("=== 聊天室使用说明 ===")
print("1. 直接输入消息:群聊")
print("2. @用户名 消息:私聊")
print("3. 输入 exit 退出聊天室")
print("======================")
while True:
try:
send_msg = input("> ").strip()
if send_msg.lower() == 'exit':
break
if send_msg:
client_socket.send(send_msg.encode('utf-8'))
except Exception as e:
print(f"发送消息异常:{e}")
break
# 6. 退出聊天室
client_socket.close()
print("已退出聊天室")
if __name__ == "__main__":
main()
运行说明
- 简单 TCP / 带时间戳示例:先启动服务器脚本,再启动客户端脚本,客户端可看到服务器消息。
- 多人聊天室示例 :
- 第一步:启动
server_chat.py;
- 第二步:启动多个
client_chat.py(模拟多用户),输入不同用户名;
- 第三步:客户端输入消息(群聊直接输,私聊输
@用户名 消息),可看到实时通讯效果。
注意事项
- 测试时若服务器和客户端不在同一台电脑,需将
server_host 改为服务器的实际 IP(如 192.168.1.100);
- 端口被占用时,可修改
port 为其他未被占用的端口(如 8888、7777);
- 聊天室简化版未加线程锁,高并发下可能有数据错乱,实际开发需加
threading.Lock() 保护全局字典。
五、常用网络协议与对应 Python 模块
| 协议 |
功能用处 |
端口号 |
Python 模块 |
| HTTP |
网页访问 |
80 |
httplib, urllib, xmlrpclib |
| NNTP |
阅读和张贴新闻文章(帖子) |
119 |
nntplib |
| FTP |
文件传输 |
20 |
ftplib, urllib |
| SMTP |
发送邮件 |
25 |
smtplib |
| POP3 |
接收邮件 |
110 |
poplib |
| IMAP4 |
获取邮件 |
143 |
imaplib |
| Telnet |
命令行远程访问 |
23 |
telnetlib |
| Gopher |
信息查找 |
70 |
gopherlib, urllib |
六、核心流程总结
1. TCP 服务器开发流程
- 创建套接字:
socket.socket(AF_INET, SOCK_STREAM)。
- 配置选项(可选):如端口重用
setsockopt()。
- 绑定地址:
bind((host, port))。
- 启动监听:
listen(backlog)。
- 循环等待连接:
accept() 获取客户端套接字和地址。
- 通讯循环:
recv() 接收数据、send()/sendall() 发送数据。
- 关闭连接:
close()(客户端 / 服务器)。
2. TCP 客户端开发流程
- 创建套接字:
socket.socket(AF_INET, SOCK_STREAM)。
- 连接服务器:
connect((host, port))。
- 通讯循环:
send() 发送数据、recv() 接收响应。
- 关闭套接字:
close()。