利用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日

相关文章

  • DedeCMS V5.3自定义模型使用教程详解

    DedeCMS V5.3自定义模型使用教程详解 概述 DedeCMS V5.3是一款基于PHP和MySQL的内容管理系统。其特点是开放源码,轻量级、高效,可快速搭建各类网站。DedeCMS V5.3提供了自定义模型功能,可通过扩展模型实现更加灵活的内容管理。本文将详细介绍如何使用自定义模型功能。 步骤 第一步:设置自定义模型 在DedeCMS V5.3后台管…

    other 2023年6月25日
    00
  • 常用Raspberry Pi周边传感器的使用教程

    常用Raspberry Pi周边传感器的使用教程 Raspberry Pi是一款非常流行的小型电脑,它的存在使得开发者们能够便捷地搭建各种自己的小型项目。其中,传感器作为Raspberry Pi的常见周边设备,能够以其简单、易用的特性为我们的项目提供全面的控制、监测、实时数据记录等功能。本篇文章将会介绍一些常用的Raspberry Pi周边传感器,如何使用它…

    其他 2023年3月28日
    00
  • 详解Angular组件生命周期(一)

    Angular组件生命周期是指一个组件从创建到销毁的整个生命周期,包含了多个钩子函数,可以在不同的组件生命周期阶段执行不同的操作,让我们更好地控制组件的行为。本文将详细讲解Angular组件生命周期的一部分,包括OnInit、OnChanges、DoCheck等常用的钩子函数。 OnInit OnInit是一个当Angular组件初始化时会自动执行的钩子函数…

    other 2023年6月27日
    00
  • MySQL优化案例之隐式字符编码转换

    MySQL优化案例之隐式字符编码转换是一个涉及MySQL字符集和编码的优化技巧,能够帮助开发者避免隐式字符编码转换带来的性能影响。 以下是MySQL优化案例之隐式字符编码转换的完整攻略: 背景和问题 MySQL中字符集和编码是非常重要的概念,不同的字符集和编码对查询和存储的性能影响很大,甚至会带来莫名其妙的问题。在MySQL中,如果查询语句中涉及到多个字段或…

    other 2023年6月25日
    00
  • vue.js移动端tab组件的封装实践实例

    下面是详细讲解“vue.js移动端tab组件的封装实践实例”的完整攻略。 1. 准备工作 在真正开始封装tab组件之前,我们需要先准备好环境和工具。 确保你的开发环境已经安装了Node.js。 安装vue.js框架,可以使用Vue-cli来构建项目。 安装webpack,可以使用Vue-cli自带的webpack配置。 2. 定义业务需求 在进行组件的封装之…

    other 2023年6月25日
    00
  • C++ 类的继承与派生实例详解

    C++ 类的继承与派生实例详解 一、什么是继承与派生 在面向对象的编程中,继承与派生是两个很重要的概念。通过继承,我们可以在已有的类的基础上,创建一个子类,并且让子类保留父类的功能和特征,然后在子类中再添加自己的功能和特征。这就是继承的意义所在。 派生是继承的一种实现方式。通过派生,子类可以从父类中继承所有的属性和方法,包括公有(public)、私有(pri…

    other 2023年6月26日
    00
  • 一文搞懂hmm(隐马尔可夫模型)

    一文搞懂HMM(隐马尔可夫模型) 什么是隐马尔可夫模型? 隐马尔可夫模型(HMM)是一种广泛应用于序列分析的统计模型,其中隐藏的状态序列进一步产生观测序列。该模型有许多应用领域,包括语音识别、自然语言处理、生物信息学、机器翻译等等。 隐马尔可夫模型由两个部分组成:1. 隐藏的状态序列,表示为 $S={s_1, s_2, …, s_n}$,其中 $n$ 是…

    其他 2023年3月28日
    00
  • 前端css基础

    前端CSS基础攻略 CSS是前端开发中不可或缺的一部分,它用于控制网页的样式和布局。本攻略将介绍CSS的基础知识,包括选择器、样式、布局等内容。 选择器 选择器用于选择要应用样式的HTML元素。以下是一些常见的选择器: 标签选择器:选择所有指定标签的元素。例如,p选择所有<p>元素。 类选择器:选择所有指定类的元素。例如,.example选择所有…

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