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

yizhihongxing

下面就是详细的 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日

相关文章

  • Python dict的使用误区你知道吗

    当我们使用Python开发过程中,使用Python字典是非常常见的。然而,许多开发者存在着困惑和误区,这会导致他们在编写代码的过程中出现各种问题。下面是Python dict使用误区的攻略。 误区1:认为字典是按顺序的 Python字典是无序的。虽然你在Python3.7中会发现字典数据会按特定的顺序打印,但实际上并没有保证字典的顺序。如果你需要按特定顺序处…

    python 2023年5月13日
    00
  • Python机器学习之决策树和随机森林

    Python机器学习之决策树和随机森林 决策树和随机森林是机器学习领域中常用的算法,在分类和回归问题中均有广泛的应用。本文将介绍如何使用Python中的scikit-learn库来建立决策树和随机森林模型。 1.决策树 1.1 算法概述 决策树是一种基于树结构的贪心算法,通过不断地将数据分成小的子集,最终生成一个树形结构。在树中,每个节点代表一个判断条件,根…

    python 2023年6月3日
    00
  • 在Python中实现shuffle给列表洗牌

    要在Python中实现给列表洗牌的功能,可以使用random模块中的shuffle函数。以下是实现的完整攻略。 步骤 导入random模块 import random 定义列表 mylist = [1, 2, 3, 4, 5] 使用shuffle函数洗牌 random.shuffle(mylist) 输出洗牌后的列表 print(mylist) 示例 以下是…

    python 2023年6月3日
    00
  • python向字符串中添加元素的实例方法

    Python中,字符串是一个不可改变的序列。因此,你不能直接向字符串中添加元素,但是你可以通过创建新字符串的方法来向字符串中添加字符。 在Python中,字符串有一个名为join的方法,用于将一些字符串连接成为一个新的字符串。join方法将一个字符串列表作为参数,返回一个将列表元素连接起来的新字符串。 以下是join方法的语法: string = str.j…

    python 2023年6月5日
    00
  • python selenium爬取斗鱼所有直播房间信息过程详解

    Python Selenium爬取斗鱼所有直播房间信息过程详解 本攻略将介绍如何使用Python Selenium爬取斗鱼所有直播房间信息。我们将使用Selenium库模拟浏览器行为,并使用BeautifulSoup库解析HTML响应。 安装Selenium和BeautifulSoup库 在开始前,我们需要安装Selenium和BeautifulSoup库。…

    python 2023年5月15日
    00
  • numpy如何获取array中数组元素的索引位置

    要获取numpy数组中元素的索引位置可以使用numpy模块中的where()函数。下面是获取索引位置的详细攻略: 步骤1:导入numpy模块 import numpy as np 步骤2:创建numpy数组 arr = np.array([10, 20, 30, 40, 50]) 步骤3:使用where()函数获取数组元素索引位置 index = np.wh…

    python 2023年6月5日
    00
  • python3.x如何向mysql存储图片并显示

    完整攻略分为以下几个步骤: 1.建立数据库连接 首先需要在Python环境中安装并导入PyMySQL库,用于连接MySQL数据库。然后使用connect()方法建立与数据库服务器的连接,即 import pymysql db = pymysql.connect(host="localhost",user="root",…

    python 2023年5月20日
    00
  • python控制nao机器人身体动作实例详解

    Python控制Nao机器人身体动作实例详解 简介 在本文中,将会详细讲解如何使用Python控制Nao机器人的身体动作。Nao机器人是一种可爱的机器人,其身体由许多舵机控制,可以进行各种动作,包括走路、舞蹈、打招呼等。在这里,我们将使用Python编程语言控制Nao机器人进行一些有趣的动作。 前置条件 在开始之前,您需要准备如下条件: 一台Nao机器人 一…

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