Python进阶--网络编程入门

一、Python 网络编程概述

1.1 什么是网络编程

网络编程又叫套接字编程,Socket编程,通信双方都有自己的Socket对象,数据在俩个Socket之间通过数据报包或者IO流的方式传输

网络编程是指通过编写代码,让两台或多台设备(计算机、手机等)通过网络进行数据传输和通信的过程。在 Python 中,我们可以利用内置的 socket 模块快速实现网络通信。

1.2 Python 中的网络编程支持

Python 提供了丰富的网络编程库,其中最核心、最基础的是 socket模块 ,它封装了底层的网络通信 API,让我们可以用简洁的代码实现 TCP、UDP 等协议的通信。此外,还有 httpurllib 等高级模块用于特定场景的网络开发。


二、网络编程核心三要素

要实现网络通信,必须明确三个核心要素:IP 地址端口号传输协议

2.1 IP 地址:标识网络中的设备

  • 作用:IP 地址是网络中设备的"身份证号",用于唯一标识一台网络设备。
  • 分类
    • IPv4 :如 127.0.0.1(本地回环地址,用于本机测试)、192.168.1.1(局域网地址)。
    • IPv6 :更长的地址格式,用于解决 IPv4 地址耗尽问题(如 ::1 是 IPv6 的本地回环地址)。

2.2 端口号:标识设备上的应用程序

  • 作用:一台设备上可能运行多个网络程序(如浏览器、微信、QQ),端口号用于区分不同的应用程序。
  • 范围:0 ~ 65535
    • 知名端口(0 ~ 1023):固定分配给常用服务,如 HTTP(80)、HTTPS(443)、SSH(22)。
    • 注册端口(1024 ~ 49151):可由用户程序使用。
    • 动态端口(49152 ~ 65535):通常由操作系统临时分配给客户端程序。

2.3 传输协议:TCP 与 UDP

传输协议规定了数据在网络中传输的规则,最常用的是 TCPUDP,两者对比如下:

|----------|----------------------------|---------------------------|
| 特性 | TCP(传输控制协议) | UDP(用户数据报协议) |
| 连接性 | 面向连接(需三次握手建立连接) | 无连接(无需建立连接,直接发数据) |
| 可靠性 | 可靠传输(保证数据不丢失、不重复、按序到达) | 不可靠传输(不保证数据到达,也不保证顺序) |
| 传输效率 | 较低(因需维护连接状态和可靠性机制) | 较高(无需额外开销) |
| 适用场景 | 文件传输、网页浏览、邮件等对可靠性要求高的场景 | 视频通话、直播、游戏等对实时性要求高的场景 |


三、Socket 编程基础

3.1 什么是 Socket

Socket(套接字)是网络通信的"端点",是两台设备之间进行数据传输的接口。可以把 Socket 想象成一个"插座",服务器和客户端通过各自的"插座"建立连接并传输数据。

3.2 Python 的 socket 模块

使用前需导入模块:

复制代码
import socket

3.3 Socket 的创建与基本参数

通过 socket.socket() 函数创建 Socket 对象,语法如下:

复制代码
socket_obj = socket.socket(family=socket.AF_INET, 
                           type=socket.SOCK_STREAM, 
                           proto=0)
  • family(地址族)
    • socket.AF_INET:使用 IPv4 地址(最常用)。
    • socket.AF_INET6:使用 IPv6 地址。
  • type(Socket 类型)
    • socket.SOCK_STREAM:面向连接的 TCP 协议(默认)。
    • socket.SOCK_DGRAM:无连接的 UDP 协议。
  • proto(协议):通常填 0,由系统自动选择合适的协议。

四、TCP 网络编程(可靠传输)

4.1 TCP 协议简介与三次握手

TCP 是面向连接的协议,通信前需通过"三次握手"建立连接:

  1. 客户端向服务器发送 SYN 包(请求建立连接)。
  2. 服务器回复 SYN+ACK 包(确认请求,并同步序列号)。
  3. 客户端回复 ACK 包(确认连接建立)。

连接建立后,双方才能进行数据传输;传输结束后,还需通过"四次挥手"断开连接。

4.2 TCP 服务器实现步骤与代码

TCP 服务器的核心流程:创建 Socket → 绑定 IP 和端口 → 监听连接 → 接受连接 → 收发数据 → 关闭连接

完整代码示例
复制代码
import socket

def tcp_server():
    # 1. 创建 TCP Socket(AF_INET: IPv4, SOCK_STREAM: TCP)
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2. 绑定 IP 地址和端口号
    # '127.0.0.1' 表示仅本机可访问,若要局域网访问可填本机局域网 IP
    # 端口号建议选 1024 以上的注册端口
    server_addr = ('127.0.0.1', 8888)
    server_socket.bind(server_addr)
    
    # 3. 开始监听连接
    # 参数 5 表示最大等待连接数(即最多允许 5 个客户端在队列中等待)
    server_socket.listen(5)
    print(f"[TCP 服务器] 已启动,监听地址:{server_addr}")
    
    # 4. 等待客户端连接(accept() 会阻塞,直到有客户端连接)
    # accept() 返回两个值:
    #   - client_socket:专门用于与该客户端通信的新 Socket
    #   - client_addr:客户端的(IP, 端口)地址
    client_socket, client_addr = server_socket.accept()
    print(f"[TCP 服务器] 客户端 {client_addr} 已连接")
    
    try:
        # 5. 接收客户端发送的数据
        # recv(1024) 表示一次最多接收 1024 字节(1KB)的数据
        # 注意:recv() 返回的是 bytes 类型,需要用 decode() 解码为字符串
        recv_data = client_socket.recv(1024)
        if recv_data:
            print(f"[TCP 服务器] 收到客户端数据:{recv_data.decode('utf-8')}")
        
        # 6. 向客户端发送数据
        # 注意:send() 只能发送 bytes 类型,需要用 encode() 编码
        send_data = "你好,客户端!我是 TCP 服务器。".encode('utf-8')
        client_socket.send(send_data)
        print(f"[TCP 服务器] 已向客户端发送数据")
    
    finally:
        # 7. 关闭与客户端通信的 Socket
        client_socket.close()
        # 8. 关闭服务器监听 Socket
        server_socket.close()
        print("[TCP 服务器] 已关闭")

if __name__ == "__main__":
    tcp_server()

4.3 TCP 客户端实现步骤与代码

TCP 客户端的核心流程:创建 Socket → 连接服务器 → 收发数据 → 关闭连接

完整代码示例
复制代码
import socket

def tcp_client():
    # 1. 创建 TCP Socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2. 连接服务器(指定服务器的 IP 和端口)
    server_addr = ('127.0.0.1', 8888)
    client_socket.connect(server_addr)
    print(f"[TCP 客户端] 已连接到服务器 {server_addr}")
    
    try:
        # 3. 向服务器发送数据
        send_data = "你好,服务器!我是 TCP 客户端。".encode('utf-8')
        client_socket.send(send_data)
        print(f"[TCP 客户端] 已向服务器发送数据")
        
        # 4. 接收服务器回复的数据
        recv_data = client_socket.recv(1024)
        if recv_data:
            print(f"[TCP 客户端] 收到服务器回复:{recv_data.decode('utf-8')}")
    
    finally:
        # 5. 关闭 Socket
        client_socket.close()
        print("[TCP 客户端] 已关闭")

if __name__ == "__main__":
    tcp_client()

4.4 实战:TCP 双向循环通信

上面的代码只能收发一次数据,我们可以用 while 循环实现双向持续通信(直到一方输入"exit"退出)。

改进后的服务器代码
复制代码
import socket

def tcp_server():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('127.0.0.1', 8888))
    server_socket.listen(5)
    print("[TCP 服务器] 等待客户端连接...")
    
    client_socket, client_addr = server_socket.accept()
    print(f"[TCP 服务器] 客户端 {client_addr} 已连接")
    
    try:
        while True:
            # 接收客户端消息
            recv_data = client_socket.recv(1024)
            if not recv_data:  # 客户端断开连接时,recv() 返回空 bytes
                break
            recv_msg = recv_data.decode('utf-8')
            print(f"[客户端] {recv_msg}")
            
            if recv_msg == "exit":
                print("[TCP 服务器] 客户端请求退出")
                break
            
            # 服务器输入回复
            send_msg = input("[服务器] 请输入回复:")
            client_socket.send(send_msg.encode('utf-8'))
            if send_msg == "exit":
                break
    
    finally:
        client_socket.close()
        server_socket.close()
        print("[TCP 服务器] 已关闭")

if __name__ == "__main__":
    tcp_server()
改进后的客户端代码
复制代码
import socket

def tcp_client():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(('127.0.0.1', 8888))
    print("[TCP 客户端] 已连接到服务器")
    
    try:
        while True:
            # 客户端输入消息
            send_msg = input("[客户端] 请输入消息:")
            client_socket.send(send_msg.encode('utf-8'))
            if send_msg == "exit":
                break
            
            # 接收服务器回复
            recv_data = client_socket.recv(1024)
            if not recv_data:
                break
            recv_msg = recv_data.decode('utf-8')
            print(f"[服务器] {recv_msg}")
            if recv_msg == "exit":
                break
    
    finally:
        client_socket.close()
        print("[TCP 客户端] 已关闭")

if __name__ == "__main__":
    tcp_client()

4.5 进阶:TCP 文件/图片传输

TCP 可靠传输的特性非常适合传输文件(如图片、文档、视频)。核心思路是:先发送文件名,再分块发送文件内容

TCP 文件传输服务器(接收端)
复制代码
import socket

def tcp_file_server():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('127.0.0.1', 8888))
    server_socket.listen(5)
    print("[TCP 文件服务器] 等待客户端连接...")
    
    client_socket, client_addr = server_socket.accept()
    print(f"[TCP 文件服务器] 客户端 {client_addr} 已连接")
    
    try:
        # 1. 先接收文件名(假设文件名不超过 1024 字节)
        file_name = client_socket.recv(1024).decode('utf-8')
        print(f"[TCP 文件服务器] 准备接收文件:{file_name}")
        
        # 2. 分块接收文件内容并写入本地
        # 文件名前加 'recv_' 前缀,避免覆盖原文件
        with open(f'recv_{file_name}', 'wb') as f:
            while True:
                # 每次接收 1024 字节(1KB)
                file_data = client_socket.recv(1024)
                if not file_data:  # 数据接收完毕
                    break
                f.write(file_data)
        
        print(f"[TCP 文件服务器] 文件 {file_name} 接收完成!")
    
    finally:
        client_socket.close()
        server_socket.close()
        print("[TCP 文件服务器] 已关闭")

if __name__ == "__main__":
    tcp_file_server()
TCP 文件传输客户端(发送端)
复制代码
import socket
import os

def tcp_file_client():
    # 要发送的文件路径(请确保该文件存在,例如同目录下的 'test.jpg')
    file_path = 'test.jpg'
    
    # 检查文件是否存在
    if not os.path.exists(file_path):
        print(f"[TCP 文件客户端] 错误:文件 {file_path} 不存在")
        return
    
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(('127.0.0.1', 8888))
    print("[TCP 文件客户端] 已连接到服务器")
    
    try:
        # 1. 先发送文件名(从路径中提取文件名)
        file_name = os.path.basename(file_path)
        client_socket.send(file_name.encode('utf-8'))
        print(f"[TCP 文件客户端] 准备发送文件:{file_name}")
        
        # 2. 分块读取文件并发送
        with open(file_path, 'rb') as f:
            while True:
                # 每次读取 1024 字节
                file_data = f.read(1024)
                if not file_data:  # 文件读取完毕
                    break
                client_socket.send(file_data)
        
        print(f"[TCP 文件客户端] 文件 {file_name} 发送完成!")
    
    finally:
        client_socket.close()
        print("[TCP 文件客户端] 已关闭")

if __name__ == "__main__":
    tcp_file_client()

五、UDP 网络编程(不可靠传输)

5.1 UDP 协议简介

UDP 是无连接的协议,通信前不需要建立连接,直接向指定的(IP, 端口)发送数据即可。虽然不可靠,但传输速度快,适合实时性要求高的场景。

5.2 UDP 服务器实现步骤与代码

UDP 服务器的核心流程:创建 Socket → 绑定 IP 和端口 → 直接收发数据(无需监听/接受连接)

完整代码示例(带详细注释)
复制代码
import socket

def udp_server():
    # 1. 创建 UDP Socket(SOCK_DGRAM: UDP)
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 2. 绑定 IP 和端口
    server_addr = ('127.0.0.1', 8888)
    server_socket.bind(server_addr)
    print(f"[UDP 服务器] 已启动,监听地址:{server_addr}")
    
    while True:
        # 3. 接收数据(recvfrom() 会阻塞)
        # recvfrom() 返回两个值:
        #   - recv_data:接收到的 bytes 数据
        #   - client_addr:发送方的(IP, 端口)地址
        recv_data, client_addr = server_socket.recvfrom(1024)
        recv_msg = recv_data.decode('utf-8')
        print(f"[UDP 服务器] 收到来自 {client_addr} 的消息:{recv_msg}")
        
        if recv_msg == "exit":
            print("[UDP 服务器] 收到退出指令")
            break
        
        # 4. 向发送方回复数据(需指定对方地址)
        send_msg = f"已收到你的消息:{recv_msg}".encode('utf-8')
        server_socket.sendto(send_msg, client_addr)
    
    # 5. 关闭 Socket
    server_socket.close()
    print("[UDP 服务器] 已关闭")

if __name__ == "__main__":
    udp_server()

5.3 UDP 客户端实现步骤与代码

UDP 客户端的核心流程:创建 Socket → 直接向服务器发送数据 → 接收回复

完整代码示例(带详细注释)
复制代码
import socket

def udp_client():
    # 1. 创建 UDP Socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 服务器地址
    server_addr = ('127.0.0.1', 8888)
    
    while True:
        # 2. 输入并发送消息
        send_msg = input("[UDP 客户端] 请输入消息:")
        client_socket.sendto(send_msg.encode('utf-8'), server_addr)
        
        if send_msg == "exit":
            break
        
        # 3. 接收服务器回复
        recv_data, _ = client_socket.recvfrom(1024)
        print(f"[UDP 客户端] 收到服务器回复:{recv_data.decode('utf-8')}")
    
    # 4. 关闭 Socket
    client_socket.close()
    print("[UDP 客户端] 已关闭")

if __name__ == "__main__":
    udp_client()

六、网络编程常见问题与注意事项

6.1 编码与解码(str ↔ bytes)

  • 关键点 :Python 3 中,socketsend()recv() 方法只能处理 bytes类型,不能直接处理字符串。
  • 转换方法
    • 字符串 → bytes:"你好".encode('utf-8')
    • bytes → 字符串:b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode('utf-8')
  • 建议 :统一使用 utf-8 编码,避免乱码。
  • 细节:
    1. 编码:把我们看懂的 转成 我们看不懂的.
      '字符串'.encode(码表)
    2. 解码:把我们看不懂的 转成 我们看懂的.
      二进制.decode(码表)
    3. 只要乱码了, 原因只有1个, 编解码不同.
    4. 英文字母, 数字, 特殊符号无论什么码表都只占1个字节, 中文在gbk占2个字节, 在utf-8中占3个字节.
    5. 二进制数据特殊写法, 即: b'字母 数字 特殊符号', 该方式针对于中文无效.

6.2 端口占用问题

  • 现象 :运行服务器时提示 OSError: [Errno 48] Address already in use
  • 解决方法
    1. 更换端口号:将代码中的端口号(如 8888)改为其他未被占用的端口。
    2. 杀掉占用端口的进程
      • Windows:打开命令行,输入 netstat -ano | findstr "8888" 找到进程 PID,再输入 taskkill /PID <PID> /F 杀掉进程。
      • Linux/Mac:输入 lsof -i:8888 找到进程 PID,再输入 kill -9 <PID> 杀掉进程。

6.3 数据缓冲区与 recv() 参数

  • recv(1024) 中的 1024 表示一次最多接收 1024 字节,不是必须每次都收满 1024 字节。
  • 如果数据量较大(如大文件),应使用 while 循环分块接收,直到数据为空。

6.4 文件传输的完整性

  • 上面的 TCP 文件传输代码是基础版本,实际应用中建议:
    1. 先发送文件大小:接收端根据文件大小判断是否接收完毕。
    2. MD5 校验:发送端发送文件的 MD5 值,接收端接收后计算 MD5 并对比,确保文件未损坏。

七、总结与回顾

  1. 网络编程三要素:IP 地址(标识设备)、端口号(标识应用)、传输协议(TCP/UDP)。
  2. Socket:网络通信的端点,是实现网络编程的核心工具。
  3. TCP 编程:面向连接、可靠传输,流程为"服务器 bind → listen → accept,客户端 connect",适合文件传输等场景。
  4. UDP 编程:无连接、不可靠传输,流程为"直接 sendto/recvfrom",适合视频通话等实时场景。
  5. 注意事项:编码解码、端口占用、分块收发数据。

相关推荐
XLYcmy2 小时前
2026游戏安全技术竞赛-PC客户端安全-初赛 求解起点到终点的最短路径
windows·python·网络安全·dfs·bfs·游戏安全·曼哈顿距离
尘埃落定wf2 小时前
FastAPI 鉴权怎么写?中间件和依赖注入一次说清楚
python·中间件·fastapi
2301_773553622 小时前
构建 Go CLI 应用的最佳实践:纯 Go 交互式命令行库选型与使用指南
jvm·数据库·python
qq_372906932 小时前
c#如何添加按钮点击事件_c#添加按钮点击事件的几种常见用法
jvm·数据库·python
2301_817672262 小时前
JavaScript 中高效定位二维数组间不匹配元素的行列索引
jvm·数据库·python
2401_831419442 小时前
golang如何实现验证码图片生成_golang验证码图片生成实现实战
jvm·数据库·python
LiAo_1996_Y2 小时前
CSS实现多列等高布局_浮动布局的高级处理技巧
jvm·数据库·python
Jenlybein2 小时前
用 uv 替代 conda,速度飙升(从 0 到 1 开始使用 uv)
后端·python·算法
格林威2 小时前
面阵相机 vs 线阵相机:堡盟与海康相机选型差异全解析 附Python实战演示
开发语言·人工智能·python·数码相机·计算机视觉·视觉检测·工业相机