Python 教程 / 网络编程

网络编程

网络编程是Python的重要应用之一,本章介绍网络基础、TCP/UDP编程和socket套接字的使用。

网络基础概念

在深入socket编程之前,我们需要了解一些基本的网络概念。

OSI七层模型 vs TCP/IP四层模型

┌─────────────────────────────────────────────────────────────────┐
│                         OSI七层模型                               │
├─────────┬───────────────────────────────────────────────────────┤
│  应用层   │                    应用层 (HTTP, FTP, SMTP)              │
│  表示层   │                   (数据格式转换、加解密)                    │
│  会话层   │                 (会话管理、建立/维护连接)                   │
│  传输层   │                    (TCP/UDP、端口)                        │
│  网络层   │                      (IP、路由)                          │
│  链路层   │                  (MAC地址、数据帧传输)                     │
│  物理层   │                    (物理介质、比特流)                      │
└─────────┴───────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                       TCP/IP四层模型                              │
├─────────┬───────────────────────────────────────────────────────┤
│  应用层   │              HTTP, FTP, SMTP, DNS                     │
│  传输层   │                    TCP, UDP                             │
│  网络层   │                      IP                                │
│  网络接口层 │              Ethernet, Wi-Fi, PPP                     │
└─────────┴───────────────────────────────────────────────────────┘ 

IP地址与端口

IP地址用于唯一标识网络中的设备,端口号用于标识设备上的具体应用程序。

┌──────────────────────────────────────────────────────┐
│              一台计算机上的多个网络应用                    │
├──────────────────────────────────────────────────────┤
│                                                      │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐     │
│   │  浏览器   │    │   QQ     │    │  邮件    │     │
│   │  :8080   │    │  :45678  │    │  :110   │     │
│   └────┬─────┘    └────┬─────┘    └────┬─────┘     │
│        │               │               │            │
│   ┌────┴───────────────┴───────────────┴─────┐      │
│   │          IP: 192.168.1.100               │      │
│   │          ┌─────────────────────┐          │      │
│   │          │   网络接口 (网卡)    │          │      │
│   │          └─────────────────────┘          │      │
│   └───────────────────────────────────────────┘      │
│                                                      │
│   端口范围: 0 ~ 65535                                │
│   0-1023: 特权端口 (需要管理员权限)                    │
│   1024+: 普通端口 (任意用户可用)                       │
└──────────────────────────────────────────────────────┘ 

TCP vs UDP对比

特性 TCP UDP
连接类型 面向连接 无连接
可靠性 可靠传输 不可靠传输
数据传输 字节流 数据报文
速度 较慢 较快
资源消耗 较多 较少
适用场景 文件传输、网页、邮件 视频通话、DNS、在线游戏

socket简介

Socket(套接字)是网络通信的基础,Python标准库的socket模块提供了丰富的网络编程接口。

┌─────────────────────────────────────────────────────────┐
│                  Socket通信原理                           │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   ┌─────────────┐           网络           ┌─────────────┐│
│   │  客户端程序  │◀═══════════════════════▶│  服务器程序  ││
│   │             │      socket()           │             ││
│   │  socket()   │         │              │  socket()   ││
│   │      │      │         │              │      │      ││
│   │ connect()───┼─────────┼──────────────┼───bind()    ││
│   │      │      │         │              │      │      ││
│   │      │      │    TCP三次握手        │ listen()    ││
│   │      │      │         │              │      │      ││
│   │      │      │         ▼              │ accept()   ││
│   │ send()/recv()◀═══════════════════════▶send()/recv()││
│   │      │      │                         │      │      ││
│   │ close()─────┼─────────────────────────┼───close()   ││
│   └─────────────┘                         └─────────────┘│
│                                                         │
│   地址: 192.168.1.100:8080  ◀═════════════▶  :8080     │
│                                                         │
└─────────────────────────────────────────────────────────┘ 
Python
import socket

# 创建TCP socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 创建UDP socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 常用socket方法
# socket()          创建socket对象
# bind((host, port))绑定地址和端口
# listen(backlog)   监听连接 (TCP)
# accept()          接受连接 (TCP)
# connect()         连接服务器 (TCP)
# connect_ex()      连接服务器,返回错误码
# send()            发送数据 (TCP)
# sendto()          发送数据 (UDP)
# recv(bufsize)     接收数据
# recvfrom()        接收数据和发送方地址 (UDP)
# close()           关闭socket
# shutdown(how)     关闭读写半关闭
# setblocking(True/False) 设置阻塞模式
# settimeout(timeout)    设置超时时间 

TCP服务器编程

TCP服务器需要绑定端口、监听连接、接受连接、处理请求、关闭连接。

┌─────────────────────────────────────────────────────────┐
│                  TCP服务器工作流程                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   1. 创建socket                                          │
│          │                                               │
│          ▼                                               │
│   2. bind() ──绑定IP和端口──▶  (192.168.1.100:8080)     │
│          │                                               │
│          ▼                                               │
│   3. listen() ──开始监听──▶  等待客户端连接...           │
│          │                                               │
│          ▼                                               │
│   4. accept() ◀──阻塞等待──▶  客户端连接请求            │
│          │                                               │
│          ▼                                               │
│   5. recv/send ◀──双向通信──▶  处理业务逻辑             │
│          │                                               │
│          ▼                                               │
│   6. close() ◀──关闭连接──▶  通信完成                   │
│                                                         │
└─────────────────────────────────────────────────────────┘ 
Python
import socket
import threading

def handle_client(client_socket, client_addr):
    """处理单个客户端请求"""
    print(f"[+] 客户端连接: {client_addr}")
    try:
        while True:
            data = client_socket.recv(1024)
            if not data:
                break
            print(f"[来自 {client_addr}]: {data.decode('utf-8')}")
            # 回显消息
            response = f"服务器收到: {data.decode('utf-8')}"
            client_socket.send(response.encode("utf-8"))
    except Exception as e:
        print(f"[!] 错误: {e}")
    finally:
        client_socket.close()
        print(f"[-] 客户端断开: {client_addr}")

def tcp_server():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(("0.0.0.0", 8080))
    server_socket.listen(5)
    print("[*] TCP服务器监听中 :8080")

    try:
        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()
    except KeyboardInterrupt:
        print("\n[*] 服务器关闭")
    finally:
        server_socket.close()

# tcp_server() 
💡 提示:使用`setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)`可以避免服务器重启时出现"Address already in use"错误。

TCP客户端编程

TCP客户端需要连接服务器、发送数据、接收响应、关闭连接。

Python
import socket

def tcp_client():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        client_socket.connect(("127.0.0.1", 8080))
        print("[*] 已连接到服务器")

        while True:
            msg = input("我: ")
            if msg.lower() == "quit":
                break
            client_socket.send(msg.encode("utf-8"))
            response = client_socket.recv(1024)
            print(f"服务器: {response.decode('utf-8')}")
    except ConnectionRefusedError:
        print("[!] 无法连接到服务器")
    except Exception as e:
        print(f"[!] 错误: {e}")
    finally:
        client_socket.close()
        print("[*] 连接已关闭")

# tcp_client() 

UDP编程

UDP是无连接协议,通信更简单,但不如TCP可靠。

┌─────────────────────────────────────────────────────────┐
│                  UDP通信模型                             │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   ┌─────────────┐           网络           ┌─────────────┐│
│   │    发送方    │◀═══════════════════════▶│    接收方    ││
│   │             │      无需连接           │             ││
│   │  socket()   │         │              │  socket()   ││
│   │      │      │         │              │      │      ││
│   │      │      │    sendto()           │      │      ││
│   │      │      │         │              │      │      ││
│   │      │      │         ├──────────────┼──recvfrom()││
│   │      │      │         │              │      │      ││
│   │ recvfrom()◀─┼─────────┼──────────────┼──sendto()  ││
│   │      │      │                         │      │      ││
│   │ close()     │                         │ close()    ││
│   └─────────────┘                         └─────────────┘│
│                                                         │
│   特点:                                                  │
│   - 无需建立连接                                         │
│   - 每个数据包独立发送                                    │
│   - 不保证顺序和可靠性                                    │
│   - 更适合实时性要求高的应用                               │
│                                                         │
└─────────────────────────────────────────────────────────┘ 
Python
import socket

# ===== UDP服务器 =====
def udp_server():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(("127.0.0.1", 8081))
    print("[*] UDP服务器监听中 :8081")

    try:
        while True:
            data, addr = udp_socket.recvfrom(1024)
            print(f"[来自 {addr}]: {data.decode('utf-8')}")
            response = f"服务器收到: {data.decode('utf-8')}"
            udp_socket.sendto(response.encode("utf-8"), addr)
    except KeyboardInterrupt:
        print("\n[*] 服务器关闭")
    finally:
        udp_socket.close()

# ===== UDP客户端 =====
def udp_client():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_addr = ("127.0.0.1", 8081)

    try:
        while True:
            msg = input("我: ")
            if msg.lower() == "quit":
                break
            udp_socket.sendto(msg.encode("utf-8"), server_addr)
            data, _ = udp_socket.recvfrom(1024)
            print(f"服务器: {data.decode('utf-8')}")
    except Exception as e:
        print(f"[!] 错误: {e}")
    finally:
        udp_socket.close()

# udp_server()  # 或 udp_client() 

Python访问HTTP服务

虽然可以使用socket发送HTTP请求,但使用urllib或requests更方便。

Python
import urllib.request
import urllib.parse
import json

# ===== GET请求 =====
with urllib.request.urlopen("https://httpbin.org/get") as response:
    data = response.read().decode("utf-8")
    print(data)

# ===== 带参数的GET请求 =====
params = urllib.parse.urlencode({"name": "张三", "age": 25})
url = f"https://httpbin.org/get?{params}"
with urllib.request.urlopen(url) as response:
    print(response.read().decode("utf-8"))

# ===== POST请求 =====
data = urllib.parse.urlencode({"username": "admin", "password": "123456"})
data = data.encode("utf-8")
req = urllib.request.Request("https://httpbin.org/post", data=data)
with urllib.request.urlopen(req) as response:
    print(response.read().decode("utf-8"))

# ===== 设置请求头 =====
req = urllib.request.Request(
    "https://httpbin.org/headers",
    headers={"User-Agent": "Mozilla/5.0", "Accept": "application/json"}
)
with urllib.request.urlopen(req) as response:
    print(response.read().decode("utf-8"))

# ===== 处理超时 =====
try:
    with urllib.request.urlopen("https://httpbin.org/delay/5", timeout=3) as response:
        print(response.read().decode("utf-8"))
except urllib.error.URLError as e:
    print(f"请求超时: {e}") 

章节总结

关键知识点:

  • OSI七层模型:应用层、表示层、会话层、传输层、网络层、链路层、物理层
  • TCP特点:面向连接、可靠传输、字节流、速度较慢
  • UDP特点:无连接、不可靠传输、数据报文、速度较快
  • Socket:网络编程的基础,封装了TCP/IP协议栈的接口
  • IP地址+端口:唯一标识网络中的应用程序
  • TCP服务器流程:socket() → bind() → listen() → accept() → recv/send → close()
  • TCP客户端流程:socket() → connect() → send/recv → close()
  • UDP通信:socket() → bind() → recvfrom/sendto → close()

socket常用参数速查:

参数 说明
socket.AF_INET IPv4协议
socket.AF_INET6 IPv6协议
socket.SOCK_STREAM TCP流式socket
socket.SOCK_DGRAM UDP数据报socket

课后测验

问题1:TCP和UDP的主要区别是什么?

问题2:TCP服务器的工作流程是什么?

问题3:socket.SOCK_DGRAM表示什么类型的socket?

课后练习

练习1:TCP聊天服务器

编写一个TCP服务器,能够同时服务多个客户端,服务器广播消息给所有连接的客户端。

提示
  1. 使用线程为每个客户端服务
  2. 维护一个客户端列表
  3. 当收到消息时,向所有客户端广播
  4. 注意线程安全问题

练习2:UDP心跳检测

编写一个UDP客户端,每隔5秒向服务器发送心跳包,服务器检测超过15秒未收到心跳则判定客户端离线。

提示
  1. 使用time模块记录发送时间
  2. 维护一个字典存储客户端的最后心跳时间
  3. 使用线程定期检查超时客户端
  4. 超时时间可设为15秒
🐍 Python 在线代码编辑器
📝 运行结果