利用Python中SocketServer 实现客户端与服务器间非阻塞通信

让我来详细讲解一下“利用Python中SocketServer 实现客户端与服务器间非阻塞通信”的完整攻略。

1. 前置知识

在学习和掌握 SocketServer 实现非阻塞通信的过程中,你需要掌握以下几个基础概念:

  • socket:套接字,用于实现网络通信的API;
  • TCP/IP协议:TCP是一种面向连接的,可靠的,基于流的传输协议,而IP则是一种无连接的,不可靠的,面向分组的协议;
  • select/poll/epoll模型:用于实现非阻塞I/O的模型,select是对文件描述符的轮询,poll基于select做了修改,epoll是Linux特有的一种I/O复用模型。

2. 实现过程

接下来,我们就来具体介绍一下 SocketServer 实现非阻塞通信的过程。

2.1 创建服务器

首先,我们需要创建一个服务器,并指定一个IP地址和端口号来进行监听:

import SocketServer

class MyTCPHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        data = self.rfile.readline().strip()
        self.wfile.write('Hello, ' + data)

if __name__ == "__main__":
    HOST, PORT = "localhost", 8000

    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

在上述代码中,我们创建了一个 MyTCPHandler 类,用于处理客户端连接和交互流程,其中 handle() 方法用于接收客户端发送的数据,并通过 wfile 属性将数据写回客户端。

2.2 实现非阻塞服务

接下来,我们需要将服务器设置为非阻塞模式,实现数据的异步发送和接收。为此,我们需要使用 Python 标准库中的 select 模块来实现:

import select

if __name__ == "__main__":
    HOST, PORT = "localhost", 8000

    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    server.socket.setblocking(0)

    inputs = [server.socket]
    outputs = []

    while inputs:
        readable, _, _ = select.select(inputs, outputs, inputs)

        for s in readable:
            if s is server.socket:
                conn, addr = s.accept()
                conn.setblocking(0)
                inputs.append(conn)
            else:
                data = s.recv(1024)
                if data:
                    outputs.append(s)
                else:
                    inputs.remove(s)
                    s.close()

        for s in outputs:
            s.send('Welcome, you are connected')
            outputs.remove(s)

在上述代码中:

  • server.socket.setblocking(0) 将服务器设置为非阻塞模式;
  • inputs = [server.socket] 开始监听服务端连接和客户端数据;
  • readable, _, _ = select.select(inputs, outputs, inputs) 获取可读取的列表;
  • if s is server.socket: 如果是服务端Socket,说明有新的客户端接入,此时需要将该客户端 Socket 设置为非阻塞模式,并将该 Socket 添加到 inputs 列表中,以便我们进行数据监听;
  • data = s.recv(1024) 如果是客户端 Socket,则需要尝试接收客户端发送的数据;
  • outputs.append(s) 如果客户端发送的数据不为空,则将该 Socket 添加到 outputs 列表中,说明这个客户端有数据需要发送;
  • outputs.remove(s) 如果数据发送完成,则将该 Socket 从 outputs 列表中删除。

通过上述代码的实现,我们就可以使用 SocketServer 实现客户端与服务器间的非阻塞通信了!

2.3 示例说明

接下来,我将通过两个示例来说明 SocketServer 实现非阻塞通信的过程。

示例一

这个示例将演示如何使用 SocketServer 创建一个非阻塞服务,当客户端连接时,服务端将向客户端发送一条“欢迎”消息,客户端再向服务端发送一条消息,服务端再次向客户端发送一条“感谢”的消息。

import SocketServer
import select

class MyTCPHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        data = self.rfile.readline().strip()
        print('客户端{}:{}连接了'.format(self.client_address[0], self.client_address[1]))

        inputs = [self.request]
        outputs = []

        while inputs:
            readable, _, _ = select.select(inputs, outputs, inputs)

            for s in readable:
                if s is self.request:
                    data = self.request.recv(1024)
                    if data:
                        print('收到来自客户端{}:{}的信息:{}'.format(self.client_address[0], self.client_address[1], data.strip()))
                        outputs.append(s)
                    else:
                        print('客户端{}:{}已经断开'.format(self.client_address[0], self.client_address[1]))
                        inputs.remove(s)
                        s.close()

            for s in outputs:
                self.request.sendall('感谢你的信息'.encode())
                outputs.remove(s)

        print('客户端{}:{}离开了'.format(self.client_address[0], self.client_address[1]))

if __name__ == "__main__":
    HOST, PORT = "localhost", 8000

    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    server.socket.setblocking(0)

    server.serve_forever()

示例二

这个示例将演示如何使用 SocketServer 创建一个 HTTP 服务器,并实现异步的处理请求。当有客户端请求时,服务端将返回一个响应头和一个Hello的响应体。

import SocketServer
import select
import time

class MyHTTPHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        print('处理请求...')
        data = self.rfile.readline().strip()
        words = data.decode().split(' ')
        print(words[0], words[1])
        while True:
            try:
                line = self.rfile.readline()
                if not line.strip():
                    break
            except socket.timeout:
                print('客户端超时,连接中断')
                return
        print('header完成')
        inputs = [self.request]
        outputs = []

        while inputs:
            readable, _, _ = select.select(inputs, outputs, inputs)

            for s in readable:
                if s is self.request:
                    data = self.request.recv(1024)
                    if data:
                        outputs.append(s)
                    else:
                        print('客户端{}:{}已经断开'.format(self.client_address[0], self.client_address[1]))
                        inputs.remove(s)
                        s.close()

            for s in outputs:
                response = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nHello'
                self.request.sendall(response.encode())
                outputs.remove(s)

if __name__ == "__main__":
    HOST, PORT = "localhost", 8000

    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyHTTPHandler)
    server.socket.setblocking(0)

    server.serve_forever()

通过这两个示例,我们可以看到,在实现 SocketServer 非阻塞通信的过程中,我们需要通过 select 来对 inputs(服务端)和 outputs(客户端)进行监听,从而实现异步的数据通信。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:利用Python中SocketServer 实现客户端与服务器间非阻塞通信 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • Android实现遮罩层(蒙板)效果

    当在Android应用中需要实现遮罩层(蒙板)效果时,可以使用以下步骤: 创建遮罩层布局:首先,在应用的布局文件中创建一个遮罩层布局。这个布局将覆盖在其他视图之上,用于实现遮罩效果。可以使用FrameLayout或RelativeLayout等布局容器来实现。 示例代码: <RelativeLayout android:id=\"@+id/m…

    other 2023年9月5日
    00
  • 基于IntelliJ IDEA 13搭建Android集成开发环境(图文教程)

    基于IntelliJ IDEA 13搭建Android集成开发环境(图文教程) 本教程将详细介绍如何使用IntelliJ IDEA 13搭建Android集成开发环境。以下是完整的攻略过程: 步骤1:下载和安装IntelliJ IDEA 13 首先,你需要下载并安装IntelliJ IDEA 13。你可以从官方网站(https://www.jetbrains…

    other 2023年7月27日
    00
  • 三星A9star怎么重启?三星A9 star重启手机教程

    三星A9 star怎么重启? 重启三星A9 star可以解决某些问题,比如卡顿、应用程序无响应等现象。以下是三种重启方法: 方法一:软重启 软重启是安全的,可以尝试修复一些小问题,并不会丢失个人数据。 步骤1:按住音量减键和开关键,直到手机屏幕黑屏,手机立即关闭。 步骤2:按住电源键,使三星A9 star重新启动。 方法二:硬重启 硬重启比软重启更适合紧急情…

    other 2023年6月26日
    00
  • 解决bind错误bind:addressalreadyinuse

    以下是详细讲解“解决bind错误bind:address already in use的完整攻略”的标准Markdown格式文本: 解决bind错误bind:address already in use 在使用bind命令绑定端口时有时会出现“bind: address already in use”的错误。这个错误通常是由于端口已经被其他进程占用而导致的。…

    other 2023年5月10日
    00
  • 3dslicer中文教程(一)—下载及安装方法

    3DSlicer中文教程(一)——下载及安装方法 介绍 3DSlicer是一款功能强大的开源医学图像处理软件,主要用于医学图像处理、分析和可视化。3DSlicer支持多种格式的医学图像数据,包括CT、MRI、超声和PET等图像数据,可用于医学研究和临床实践。 本文将详细介绍3DSlicer的下载和安装方法,让大家能够轻松地使用这款软件进行医学图像处理和分析。…

    其他 2023年3月28日
    00
  • 使用代码生成器自定义Entity的部分注解

    使用代码生成器自定义Entity的部分注解,可以在生成代码时自动为实体类添加一些自定义的注解,方便我们在后续的开发中使用。具体步骤如下: 打开代码生成器,选择要生成代码的表,点击“进入高级设置”按钮。 在“模板文件设置”中选择要使用的模板文件,例如基于MyBatis-Plus的模板,选择“MP风格”或“MP风格(Kotlin)”,这些模板文件已经预定义了一些…

    other 2023年6月25日
    00
  • 通过命令行方式批量设置保留IP地址的代码

    在命令行方式下,可以通过DHCP服务器来为本网络中的主机分配IP地址。在此过程中,我们有时需要保留特定的IP地址,以便将其分配给指定的主机。下面是一份完整的攻略,教你如何通过命令行方式批量设置保留IP地址的代码。 1. 配置DHCP服务器 首先,我们需要配置DHCP服务器来设置保留IP地址。在Linux系统中,可以通过修改/etc/dhcp/dhcpd.co…

    other 2023年6月26日
    00
  • iOS10.2.1正式版升级需要多大空间 苹果新系统iOS10.2.1正式版更新升级占用多大内存

    iOS 10.2.1正式版升级所需空间攻略 苹果的新系统iOS 10.2.1正式版已经发布,如果你想升级到这个版本,你需要确保你的设备有足够的可用空间。以下是一个详细的攻略,告诉你升级到iOS 10.2.1正式版所需的空间大小。 步骤1:检查可用空间 在升级之前,首先要检查你的设备上有多少可用空间。你可以按照以下步骤进行操作: 打开设备的设置应用程序。 点击…

    other 2023年8月1日
    00
合作推广
合作推广
分享本页
返回顶部