Python网络编程使用select实现socket全双工异步通信功能示例

下面就是详细的 Python 网络编程使用 select 实现 socket 全双工异步通信功能的攻略。

1、什么是 select

select 是一种 I/O 多路复用机制,它可以监控多个文件描述符,等待输入或输出操作就绪,从而实现启用一个线程或一个进程就能同时管理多个连接通道。

2、select 的优劣

  • 优点:select 可以同时监听多个连接,无需通过多线程、多进程实现,节省了系统资源,提高了程序效率。
  • 缺点:select 在高并发条件下,处理效率会随着连接增多而下降,且开发难度较大。

3、使用 select 实现 socket 全双工异步通信

(1)创建套接字和绑定端口:

import socket

server = socket.socket()
server.bind(('localhost', 8888))
server.listen(5)

(2)准备 I/O 数据:

msg1 = b'Hello, from client1!'
msg2 = b'Hello, from client2!'

(3)创建 select 对象和加入列表:

import select

inputs = [server]
outputs = []

(4)循环接收和处理数据:

while True:
    # select 机制实现异步 I/O
    # 监听 inputs 中可读事件和写事件,超时时间设置为 5 秒
    rlist, wlist, elist = select.select(inputs, outputs, inputs, 5)
    # 遍历可读事件列表,接收客户端连接请求
    for s in rlist:
        if s is server:
            conn, addr = server.accept()
            print('新客户端连接:', addr)
            # 将新连接加入 inputs 中,以便监控其输入事件
            inputs.append(conn)
        else:
            # 从已连接的客户端接收数据并输出
            data = s.recv(1024)
            if data:
                print(data)
                # 将输出目标加入 outputs 列表
                outputs.append(s)
            else:
                # 如果客户端连接关闭,将其从 inputs 中移除
                inputs.remove(s)
                s.close()

    # 遍历可写事件列表,发送数据给客户端
    for s in wlist:
        if s in outputs:
            # 向连接的客户端发送数据
            s.sendall(msg2)
            # 将该连接从输出列表中删除
            outputs.remove(s)

    # 遍历异常事件列表,关闭连接并移除
    for s in elist:
        inputs.remove(s)
        if s in outputs:
            outputs.remove(s)
        s.close()

上述示例中,创建了一个服务器套接字server 和一个监听列表 inputs,将 server 添加到 inputs 列表中,实现异步监听连接请求。接下来,使用 select.select() 方法,将 inputs 中的 socket 对象分组,轮流检查每个可读或可写的套接字,并响应相应的数据,并在需要时添加或删除socket对象。这种管理方式提供了更高的并发处理能力,增加了服务器的稳定性和可用性。

示例中还可以通过数据包长度(recv(1024))或消息结束符(\n等)来控制数据的粘连度,这显然需要在数据发送方进行控制。

4、示例说明

示例1:使用 select 实现简单聊天室

# 聊天室服务端示例
import socket
import select

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('localhost', 10086))
server.listen(5)

inputs = [server]
outputs = []

while True:
    rlist, wlist, elist = select.select(inputs, outputs, inputs, 5)
    for s in rlist:
        if s is server:
            conn, addr = s.accept()
            print('New connection from', addr)
            inputs.append(conn)
        else:
            try:
                data = s.recv(1024)
            except:
                data = b''

            if data:
                print(data)
                for other in inputs:
                    if other != s and other != server:
                        try:
                            other.sendall(data)
                        except:
                            pass
            else:
                inputs.remove(s)
                s.close()
    for s in wlist:
        pass
    for s in elist:
        inputs.remove(s)
        if s in outputs:
            outputs.remove(s)
        s.close()

server.close()

以上代码中,服务端创建了一个socket实例,循环接收客户端请求,并实现了基于select式的异步输入输出数据。每当一个客户端 socket 实例有数据的时候,服务端就会将这个消息广播到所有连接到的 socket 实例中。

示例2:使用 select 实现批量邮件发送

# 邮件发送客户端实例
import socket
import select

def sendmail(sender, recipient, subject, content):
    # 连接邮件服务器
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.connect(('smtp.example.com', 25))
    inputs = [server]
    outputs = []
    # 接收服务器返回信息并解码为字符串
    def recv_all(sock):
        data = b""
        while True:
            try:
                more = sock.recv(1024)
                if not more:
                    break
                data += more
            except Exception as e:
                print("对方网络不稳定,连接中断!")
                data = ""
                break
        return data.decode('utf-8')
    # 发送邮件
    rlist, wlist, elist = select.select(inputs, outputs, inputs, 10)
    server.sendall(b'HELO example.com\n')
    print(recv_all(server))
    server.sendall(b'MAIL FROM:' + sender.encode() + b'\n')
    print(recv_all(server))
    server.sendall(b'RCPT TO:' + recipient.encode() + b'\n')
    print(recv_all(server))
    server.sendall(b'DATA\n')
    print(recv_all(server))
    server.sendall(b'Subject:' + subject.encode() + b'\n')
    server.sendall(b'From:' + sender.encode() + b'\n')
    server.sendall(b'To:' + recipient.encode() + b'\n\n')
    server.sendall(content.encode() + b'\n')
    server.sendall(b'.\n')
    print(recv_all(server))
    server.sendall(b'QUIT\n')
    print(recv_all(server))
    # 关闭连接
    server.close()

if __name__ == '__main__':
    sendmail('johndoe@example.com', 'janedoe@example.com', 'test email', 'hello,world!')

以上代码中,一个邮件服务是由邮件服务器和发送者和接收者两个 socket 实例组成,每个发送 socket 实例开启 select 异步输入输出数据交换机制,读入用户的 mail 消息进行后续处理。在 select 的监听机制下,实现多任务完成信道相互隔离的通信操作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python网络编程使用select实现socket全双工异步通信功能示例 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • 使用Pyhton集合set()实现成果查漏的例子

    当我们在编写代码的时候,常常需要保证数据的正确性和完整性。这就需要进行查漏操作。Python 提供了一种非常方便的方式来进行查漏,那就是使用集合 set()。本文将详细讲解如何使用 Python 集合 set() 实现成果查漏的例子。 集合 set() 概述 在开始讲解如何使用集合 set() 实现查漏之前,我们先来了解一下集合 set() 的概念。 集合是…

    python 2023年5月13日
    00
  • Python文本处理之按行处理大文件的方法

    那么让我们来详细讲解一下 “Python文本处理之按行处理大文件的方法” 这个主题。 什么是按行处理大文件 在文本处理领域中,我们经常需要从一个大文件中读取数据进行处理。但是直接读取整个大文本文件可能会导致我们的程序在内存方面出现问题,所以我们需要一种更为高效的方式来读取这些大文件。因此,我们需要按行读取这些大文件,然后进行逐行处理。 按行处理大文件的方法 …

    python 2023年6月6日
    00
  • Python抓取今日头条街拍图片数据

    下面是“Python抓取今日头条街拍图片数据”的完整攻略。 步骤一:分析目标网站 在使用Python抓取数据之前,需要先分析目标网站。以今日头条网站的街拍栏目为例,我们可以先通过浏览器的开发者工具(DevTools)观察到该栏目的API接口。在Network面板中刷新页面,找到XHR类型的请求,即可找到API接口的请求路径和参数信息。 具体来说,在今日头条街…

    python 2023年6月3日
    00
  • Python实现爬虫抓取与读写、追加到excel文件操作示例

    下面是Python实现爬虫抓取与读写、追加到excel文件操作的完整实例教程: 1. 准备工作 在开始实现爬虫抓取与读写、追加到excel文件操作之前,需要先安装以下Python库:* requests:用于发起HTTP请求并获取HTML内容;* beautifulsoup4:用于解析HTML内容;* openpyxl:用于读写Microsoft Excel…

    python 2023年5月14日
    00
  • Python函数生成器原理及使用详解

    Python函数生成器原理及使用详解 Python中的生成器是一种特殊的函数,它可以在需要时生成一系列值,而不是一次性生成所有值。生成器可以帮助我们节省内存,并提高程序的效率。本文将详细介绍Python函数生成器的原理及使用方法,并提供两个示例。 生成器的原理 生成器是一种特殊的函数,它使用yield语句返回一个值,并暂停函数的执行。当生成器被调用时,它会返…

    python 2023年5月15日
    00
  • Python ttkbootstrap 制作账户注册信息界面的案例代码

    下面是Python ttkbootstrap 制作账户注册信息界面的完整攻略: 攻略 步骤一:导入依赖库 首先,为了使用 ttkbootstrap,需要先安装它。可以通过 pip 命令进行安装: pip install ttkbootstrap 然后,在代码中导入必要的依赖库: from tkinter import * from ttkbootstrap …

    python 2023年6月13日
    00
  • 通过抓取淘宝评论为例讲解Python爬取ajax动态生成的数据(经典)

    下面是详细的攻略: 通过抓取淘宝评论为例讲解Python爬取ajax动态生成的数据 在Python中,我们可以使用requests和json模块实现爬取ajax动态生成的数据。本文将以抓取淘宝评论为例,讲解Python爬取ajax动态生成的数据的过程,并提供两个示例说明。 抓取淘宝评论的过程 在抓取淘宝评论的过程中,我们需要模拟浏览器发送请求,并解析返回的j…

    python 2023年5月14日
    00
  • Python实现图片添加文字

    下面是Python实现图片添加文字的完整攻略: 1. 准备工具和环境 首先,我们需要确保电脑中有Python环境和ImageMagick工具。可以在终端中通过以下命令检查: python –version convert -version 如果命令行输出了相应版本的信息,那么就说明已经具备了必要的工具和环境。 2. 使用Python库Pillow打开图片 …

    python 2023年6月6日
    00
合作推广
合作推广
分享本页
返回顶部