Python3 Socket 网络编程复习笔记

一、核心基础概念

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 服务器 - 客户端(单向通讯)

服务器端(server.py
  1. 导入 socket、sys 模块,创建 TCP 套接字(AF_INET + SOCK_STREAM)。
  2. 获取本地主机名,绑定端口(例:9999),设置最大连接数 5。
  3. 循环监听客户端连接,接受连接后发送欢迎语,关闭客户端套接字。
客户端(client.py
  1. 创建 TCP 套接字,连接服务器(主机名 + 端口 9999)。
  2. 接收服务器数据(最大 1024 字节),解码后打印,关闭套接字。

2. 带时间戳的 TCP 双向通讯

服务器端
  • 核心增强:设置端口重用,循环接收客户端消息,拼接时间戳后返回给客户端,捕获断开异常。
  • 关键逻辑:msg = time.strftime("%Y-%m-%d %X") + ":" + 客户端消息
客户端
  • 核心逻辑:循环输入消息发送给服务器,接收带时间戳的响应并打印,输入为空则退出。

3. 多人聊天室(支持群聊 + 私聊,多线程实现)

服务器端核心设计
  1. 数据结构:维护客户端地址 - 名称映射、名称 - 客户端映射、所有客户端列表。
  2. 多线程:每接入一个客户端启动一个线程,独立处理该客户端消息。
  3. 消息机制:
    • 群聊:接收消息后向所有客户端广播。
    • 私聊:消息以 @用户名 内容 格式,提取目标用户并定向发送。
  4. 状态管理:客户端断开时,移除该客户端,向所有用户广播退出通知。
客户端核心设计
  1. 连接服务器后发送用户名,启动独立线程监听并打印服务器消息。
  2. 主线程循环输入消息并发送,支持群聊(直接输入)和私聊(@格式输入)。

4. 代码实现

简单 TCP 服务器 - 客户端(单向通讯)
1. 服务器端(server1.py
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")
2. 客户端(client1.py
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 双向通讯
1. 服务器端(server2.py
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} 连接关闭")
2. 客户端(client2.py
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()
运行说明
  1. 简单 TCP / 带时间戳示例:先启动服务器脚本,再启动客户端脚本,客户端可看到服务器消息。
  2. 多人聊天室示例
    • 第一步:启动 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 服务器开发流程

  1. 创建套接字:socket.socket(AF_INET, SOCK_STREAM)
  2. 配置选项(可选):如端口重用 setsockopt()
  3. 绑定地址:bind((host, port))
  4. 启动监听:listen(backlog)
  5. 循环等待连接:accept() 获取客户端套接字和地址。
  6. 通讯循环:recv() 接收数据、send()/sendall() 发送数据。
  7. 关闭连接:close()(客户端 / 服务器)。

2. TCP 客户端开发流程

  1. 创建套接字:socket.socket(AF_INET, SOCK_STREAM)
  2. 连接服务器:connect((host, port))
  3. 通讯循环:send() 发送数据、recv() 接收响应。
  4. 关闭套接字:close()
相关推荐
降临-max1 小时前
JavaSE---网络编程
java·开发语言·网络·笔记·学习
赖small强2 小时前
【Linux 网络基础】libwebsockets HTTPS 服务端实现机制详解
linux·网络·https·tls·libwebsockets
大白的编程日记.2 小时前
【计算网络学习笔记】MySql的多版本控制MVCC和Read View
网络·笔记·学习·mysql
IMPYLH3 小时前
Lua 的 require 函数
java·开发语言·笔记·后端·junit·lua
shmexon4 小时前
上海兆越亮相无锡新能源盛会,以硬核通信科技赋能“能碳未来”
网络·人工智能
Lay_鑫辰5 小时前
西门子诊断-状态和错误位(“轴”工艺对象 V1...3)
服务器·网络·单片机·嵌入式硬件·自动化
车载测试工程师6 小时前
CAPL学习-IP API函数-2
网络·学习·tcp/ip·capl·canoe
Xの哲學6 小时前
Linux 指针工作原理深入解析
linux·服务器·网络·架构·边缘计算
YJlio6 小时前
进程和诊断工具学习笔记(8.29):ListDLLs——一眼看清进程里加载了哪些 DLL,谁在偷偷注入
android·笔记·学习