0

0

如何用Python进行网络编程(Socket)?

狼影

狼影

发布时间:2025-09-04 17:28:01

|

371人浏览过

|

来源于php中文网

原创

Python Socket编程中TCP与UDP的核心差异在于:TCP是面向连接、可靠的协议,适用于文件传输等需数据完整性的场景;UDP无连接、速度快,适合实时音视频、游戏等对延迟敏感的应用。选择依据是对可靠性与速度的需求权衡。

如何用python进行网络编程(socket)?

使用Python进行网络编程,核心在于其内置的

socket
模块。它提供了一套标准接口,让程序能够像打电话一样,通过IP地址和端口号与其他程序建立连接,进行数据交换。无论是构建一个简单的聊天客户端,还是复杂的服务器应用,我们都是围绕着创建socket、绑定/连接、监听/发送/接收这些基本操作来展开的。这不仅仅是技术细节,更是一种通信思维的体现,让不同的程序能够在网络空间中“对话”。

解决方案

Python的

socket
模块是进行网络编程的基石。这里我将通过一个简单的TCP客户端和服务器的例子,来展示其基本用法。

服务器端代码示例:

import socket
import threading # 后面会提到多线程,这里先引入

HOST = '127.0.0.1'  # 服务器监听的IP地址
PORT = 65432        # 服务器监听的端口

def handle_client(conn, addr):
    """处理单个客户端连接的函数"""
    print(f"Connected by {addr}")
    try:
        while True:
            data = conn.recv(1024) # 接收客户端数据,最大1024字节
            if not data: # 如果没有数据,说明客户端断开连接
                print(f"Client {addr} disconnected.")
                break
            message = data.decode('utf-8')
            print(f"Received from {addr}: {message}")

            # 回复客户端
            response = f"Server received: {message}"
            conn.sendall(response.encode('utf-8'))
    except ConnectionResetError:
        print(f"Client {addr} forcibly closed the connection.")
    except Exception as e:
        print(f"Error handling client {addr}: {e}")
    finally:
        conn.close() # 关闭客户端连接

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT)) # 绑定IP地址和端口
    s.listen() # 开始监听,等待客户端连接
    print(f"Server listening on {HOST}:{PORT}")

    while True:
        conn, addr = s.accept() # 接受新的客户端连接
        # 为每个新连接创建一个线程来处理,避免阻塞主进程
        client_thread = threading.Thread(target=handle_client, args=(conn, addr))
        client_thread.start()

客户端代码示例:

立即学习Python免费学习笔记(深入)”;

import socket

HOST = '127.0.0.1'  # 服务器的IP地址
PORT = 65432        # 服务器的端口

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    try:
        s.connect((HOST, PORT)) # 连接到服务器
        print(f"Connected to {HOST}:{PORT}")

        message_to_send = "Hello, server! This is client."
        s.sendall(message_to_send.encode('utf-8')) # 发送数据

        data = s.recv(1024) # 接收服务器的回复
        print(f"Received from server: {data.decode('utf-8')}")

    except ConnectionRefusedError:
        print("Connection refused. Is the server running?")
    except Exception as e:
        print(f"An error occurred: {e}")
    finally:
        print("Client closing connection.")
        # s.close() # with 语句块结束时会自动关闭

在这些例子中,

socket.AF_INET
指定了使用IPv4地址族,
socket.SOCK_STREAM
则表示使用TCP协议(流式套接字)。服务器通过
bind()
绑定地址,
listen()
开始监听,然后
accept()
接受连接。每个
accept()
返回一个新的套接字对象
conn
,用于与特定客户端通信,以及客户端的地址
addr
。客户端则直接使用
connect()
连接服务器。数据传输时,需要注意字符串和字节串之间的编码(
encode()
)和解码(
decode()
)。

Python Socket编程中TCP与UDP的选择与核心差异是什么?

在Python的Socket编程中,我们通常会选择TCP(传输控制协议)或UDP(用户数据报协议)。这两种协议各有优劣,选择哪一个往往取决于你的应用场景对数据可靠性和传输速度的需求。

TCP (Transmission Control Protocol) TCP是一种面向连接的协议。这意味着在数据传输之前,客户端和服务器之间必须先建立一个可靠的连接。它提供:

  • 可靠性: TCP确保数据能够无差错、按顺序地到达目的地。如果数据包丢失或损坏,TCP会自动重传。
  • 有序性: 数据会按照发送的顺序接收。
  • 流量控制: 防止发送方发送数据过快,导致接收方无法处理。
  • 拥塞控制: 避免网络过载。

何时选择TCP? 我个人在开发需要高度数据完整性的应用时,比如文件传输、网页浏览(HTTP)、电子邮件(SMTP/POP3/IMAP)或数据库连接时,总是优先考虑TCP。它的可靠性省去了很多手动处理数据丢失和乱序的麻烦,虽然会带来一些额外的开销和延迟,但这种“省心”的特性对于大多数业务应用来说是值得的。

在Python中,创建TCP Socket使用

socket.socket(socket.AF_INET, socket.SOCK_STREAM)

UDP (User Datagram Protocol) UDP是一种无连接的协议。它不保证数据的可靠传输,也不保证数据包的顺序。每个UDP数据报都是一个独立的单元,发送后就“不管不顾”了。

  • 不可靠性: 数据包可能丢失、重复或乱序。
  • 无序性: 接收到的数据包顺序可能与发送顺序不同。
  • 速度快: 由于没有建立连接的握手过程,也没有重传、流量控制等机制,UDP的传输速度通常比TCP快。
  • 开销小: 头部信息比TCP小,对系统资源占用少。

何时选择UDP? 对于那些对实时性要求极高,但可以容忍少量数据丢失的应用,UDP是更好的选择。比如在线游戏、实时音视频流(VoIP)、DNS查询等。在这种场景下,如果一个数据包丢失了,重传它反而会增加延迟,不如直接发送下一个数据包,让应用层去处理可能的“瑕疵”。在我看来,如果你需要自己实现复杂的可靠性机制,或者对延迟有极致要求,才会考虑UDP。

在Python中,创建UDP Socket使用

socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
。UDP的发送和接收方法也不同,它使用
sendto()
recvfrom()
,因为每次发送都需要指定目标地址,接收时也会返回发送方的地址。

总结来说,TCP是“严谨的信使”,确保每一封信都准确无误、按时送达;UDP则是“快速的广播员”,信息发出去就不管了,谁收到算谁的,但速度绝对快。根据你对“信息”的重视程度,做出合适的选择。

如何处理Python Socket编程中的常见错误和异常?

在Python Socket编程中,网络环境的复杂性和不可预测性意味着错误和异常是常态。不恰当的错误处理轻则导致程序崩溃,重则引发数据丢失或安全问题。我的经验是,预见并妥善处理这些异常,是构建健壮网络应用的关键。

以下是一些常见的Socket错误和它们的处理策略:

  1. ConnectionRefusedError
    (连接被拒绝):

    • 场景: 客户端尝试连接一个不存在的服务器、服务器未启动、或者服务器防火墙阻止了连接。
    • 处理: 在客户端的
      connect()
      调用周围使用
      try...except ConnectionRefusedError:
      捕获。可以尝试重试连接,或者向用户显示错误信息。
  2. ConnectionResetError
    (连接被重置):

    • 场景: 通常发生在服务器端,当客户端在没有正常关闭连接的情况下突然断开(例如,客户端程序崩溃、网络中断)。服务器尝试向已关闭的连接写入或读取时会触发。
    • 处理: 在服务器的
      recv()
      send()
      循环中捕获此异常。一旦捕获,意味着客户端已断开,应该关闭对应的客户端socket并清理资源。
  3. BrokenPipeError
    (管道破裂):

    BJXSHOP网上购物系统 - 书店版
    BJXSHOP网上购物系统 - 书店版

    BJXSHOP购物管理系统是一个功能完善、展示信息丰富的电子商店销售平台;针对企业与个人的网上销售系统;开放式远程商店管理;完善的订单管理、销售统计、结算系统;强力搜索引擎支持;提供网上多种在线支付方式解决方案;强大的技术应用能力和网络安全系统 BJXSHOP网上购物系统 - 书店版,它具备其他通用购物系统不同的功能,有针对图书销售而进行开发的一个电子商店销售平台,如图书ISBN,图书目录

    下载
    • 场景: 当你试图向一个已经关闭的socket写入数据时,会抛出此错误。它与
      ConnectionResetError
      有些相似,但通常发生在尝试
      send()
      时。
    • 处理: 类似
      ConnectionResetError
      ,在发送数据前检查连接状态,或者在
      send()
      调用周围捕获。一旦发生,说明连接已不可用。
  4. TimeoutError
    (超时错误):

    • 场景: 当socket操作(如
      connect()
      ,
      recv()
      ,
      send()
      ,
      accept()
      ) 在指定时间内没有完成时发生。
    • 处理: 可以通过
      socket.settimeout(seconds)
      方法为socket设置一个超时时间。在
      try...except TimeoutError:
      中捕获,然后决定是重试、跳过还是报告错误。这对于防止程序长时间阻塞在网络操作上非常重要。
  5. BlockingIOError
    (阻塞IO错误):

    • 场景: 当socket设置为非阻塞模式(
      socket.setblocking(False)
      )后,如果一个操作(如
      recv()
      )会阻塞,就会立即抛出此错误。
    • 处理: 这不是一个真正的错误,而是非阻塞模式下的一种预期行为。通常与
      select
      selectors
      asyncio
      等异步I/O机制结合使用,表示当前没有数据可读或不可写,需要稍后再试。
  6. OSError
    (操作系统错误):

    • 场景: 这是一个更通用的错误,
      socket.error
      在Python 3.3+中被
      OSError
      取代。它可能包含上述所有特定错误,也可能包含其他系统级别的网络错误,例如“地址已被使用”(
      Address already in use
      )当服务器尝试绑定一个已被占用的端口时。
    • 处理: 对于“地址已被使用”错误,可以尝试更改端口,或者在服务器关闭后等待一段时间再启动。对于其他通用的
      OSError
      ,通常需要查看其错误码或错误信息以进行具体判断。

通用处理策略:

  • 使用
    try...except
    块:
    这是Python处理异常的基本方式。将所有可能抛出异常的网络操作放入
    try
    块中,并在
    except
    块中处理。
  • finally
    块:
    确保在
    finally
    块中关闭socket连接和释放资源,无论是否发生异常。
    with socket.socket(...) as s:
    语句在Python 3中是更好的实践,它能自动管理socket的关闭。
  • 日志记录: 记录捕获到的异常信息,包括时间、错误类型、错误消息和相关上下文,这对于调试和问题排查至关重要。
  • 优雅关闭: 在关闭socket之前,尝试使用
    socket.shutdown(socket.SHUT_RDWR)
    来通知对端不再发送或接收数据,这有助于更优雅地断开连接。

记住,网络是不可靠的。与其期望一切顺利,不如假设一切都会出错,并为每一种可能的情况做好准备。这虽然增加了代码的复杂度,但大大提升了程序的健壮性和用户体验。

Python Socket服务器如何实现多客户端并发连接?

一个基本的Python Socket服务器,如果不做特殊处理,一次只能处理一个客户端连接。当它在

conn.recv()
conn.send()
上阻塞时,其他等待连接的客户端就只能排队。这显然无法满足现代网络应用的需求。实现多客户端并发连接,是构建实用网络服务的必经之路。

有几种主流的方法可以解决这个问题,每种方法都有其适用场景和权衡:

  1. 多线程(Threading)

    • 原理: 每当服务器接受到一个新的客户端连接(
      s.accept()
      ),就创建一个新的线程来处理这个客户端的通信。主线程继续监听新的连接。
    • 优点: 实现相对简单直观,每个客户端的处理逻辑可以独立编写。对于I/O密集型任务(即大部分时间在等待网络数据),Python的GIL(全局解释器锁)影响不大,因为线程在等待I/O时会释放GIL。
    • 缺点: 线程的创建和管理有一定开销。对于CPU密集型任务,由于GIL的存在,多线程并不能真正实现并行计算,只是并发执行。线程之间的共享数据需要加锁保护,否则可能出现竞态条件。
    • 适用场景: 中小型服务器应用,I/O操作较多,对CPU计算要求不高的场景。

    我的示例代码中已经包含了多线程的简单实现,即在

    s.accept()
    后,调用
    threading.Thread(target=handle_client, args=(conn, addr)).start()

  2. 多进程(Multiprocessing)

    • 原理: 类似多线程,但每个客户端连接会分配到一个独立的进程来处理。
    • 优点: 进程之间内存独立,避免了GIL的限制,可以真正实现并行计算。一个进程崩溃不会影响其他进程。
    • 缺点: 进程创建和销毁的开销比线程大得多,更消耗系统资源。进程间通信(IPC)比线程间通信复杂。
    • 适用场景: 需要利用多核CPU进行并行计算的服务器,或者对隔离性要求较高的服务。

    实现上,将

    threading.Thread
    替换为
    multiprocessing.Process
    即可,但需要注意进程间数据共享的问题。

  3. 异步I/O(Asynchronous I/O)

    • 原理: 使用单线程或少量线程,通过事件循环(event loop)来管理多个I/O操作。当一个I/O操作(如
      recv()
      )会阻塞时,它不会停下来等待,而是注册一个回调函数,然后去处理其他客户端的I/O事件。当之前的I/O操作完成后,事件循环会调用相应的回调函数。Python的
      asyncio
      模块是实现异步I/O的官方推荐方式,它基于协程(coroutine)。
    • 优点: 极高的并发性能和扩展性,资源消耗远低于多线程/多进程。特别适合I/O密集型任务。
    • 缺点: 编程范式与传统的同步编程有很大不同,需要使用
      async
      await
      关键字,学习曲线较陡峭。调试相对复杂。
    • 适用场景: 大规模高并发的网络服务,如Web服务器、API网关等。这是我个人在构建高性能网络服务时首选的方案。

    一个

    asyncio
    的Socket服务器大致结构如下:

    import asyncio
    
    async def handle_client_async(reader, writer):
        addr = writer.get_extra_info('peername')
        print(f"Connected by {addr}")
        try:
            while True:
                data = await reader.read(1024) # 异步读取
                if not data:
                    print(f"Client {addr} disconnected.")
                    break
                message = data.decode('utf-8')
                print(f"Received from {addr}: {message}")
    
                response = f"Server received: {message}"
                writer.write(response.encode('utf-8')) # 异步写入
                await writer.drain() # 确保数据发送完成
        except Exception as e:
            print(f"Error handling client {addr}: {e}")
        finally:
            writer.close()
            await writer.wait_closed() # 等待写入器关闭
    
    async def main_async():
        server = await asyncio.start_server(
            handle_client_async, '127.0.0.1', 65432
        )
        addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
        print(f"Serving on {addrs}")
    
        async with server:
            await server.serve_forever()
    
    # if __name__ == '__main__':
    #     asyncio.run(main_async())

选择哪种并发模型,没有绝对的答案,需要根据项目的具体需求、团队的技术栈以及预期的负载来决定。对于初学者,多线程是一个不错的起点,能够快速理解并发的机制。而对于追求极致性能和扩展性的场景,

asyncio
无疑是更现代、更强大的选择。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

716

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

627

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

742

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

617

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1236

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

575

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

699

2023.08.11

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

65

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 2.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号