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

yizhihongxing

让我来详细讲解一下“利用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日

相关文章

  • Java网页数据采集器[中篇-数据存储]

    Java网页数据采集器[中篇-数据存储] 本文将为您提供Java网页数据采集器[中篇-数据存储]的完整攻略,包括数据存储的基本概念、数据存储的方式、以及两个示例说明。 数据存储的基本概念 数据存储是指将采集到的数据保存到本地或远程服务器上,以便后续的数据处理和分析。常用的数据存储方式包括文件存储、数据库存储、以及云存储等。 数据存储的方式 Java网页数据采…

    other 2023年5月6日
    00
  • Windows Server 2012 R2或2016无法安装.NET Framework 3.5.1的解决方法

    下面是详细的攻略步骤: 1. 确认Windows Server版本 首先,需要确认所使用的Windows Server版本是2012 R2或2016版,因为只有这两个版本才会出现无法安装.NET Framework 3.5.1的问题。 2. 启用.NET Framework 3.5.1框架 在Windows Server 2012 R2或2016中,默认情况…

    other 2023年6月27日
    00
  • 网页中出现your request has bad syntax or is提示的解决方法详解

    标题:处理网页中出现”Your request has bad syntax or is inherently impossible to be satisfied”提示的解决方法 当我们在浏览网页时出现”Your request has bad syntax or is inherently impossible to be satisfied”这个提示时…

    other 2023年6月27日
    00
  • 使用C语言判断英文字符大小写的方法

    使用C语言判断英文字符的大小写有多种方法。下面是一种常见的方法: 首先,我们需要了解ASCII码表。在ASCII码表中,大写字母的ASCII码范围是65到90,小写字母的ASCII码范围是97到122。 我们可以使用条件语句来判断字符的大小写。下面是一个示例代码: #include <stdio.h> int main() { char ch; …

    other 2023年8月16日
    00
  • 深入解析C++编程中的运算符重载

    深入解析C++编程中的运算符重载 在C++中,运算符重载可以让我们自定义运算符的行为,让其适用于自定义类和数据类型。以下是深入解析C++编程中运算符重载的完整攻略。 1. 进行运算符重载 运算符重载是通过定义特殊类型的函数来实现的,这些函数的名称是由运算符自己确定的。例如,运算符+的重构函数应该被命名为operator+。下面是一个重载运算符+的例子: cl…

    other 2023年6月27日
    00
  • Android5.1系统通过包名给应用开放系统权限的方法

    Android 5.1系统通过包名给应用开放系统权限的方法攻略 在Android 5.1系统中,可以通过以下步骤给应用开放系统权限: 确定应用的包名:首先,需要确定要给应用开放权限的包名。包名是应用在Android系统中的唯一标识符,可以在应用的清单文件(AndroidManifest.xml)中找到。 编辑系统权限配置文件:接下来,需要编辑系统权限配置文件…

    other 2023年9月7日
    00
  • python中if嵌套命令实例讲解

    Python中if嵌套命令实例讲解 在Python中,我们可以使用if语句来进行条件判断。有时候,我们需要在一个条件满足的情况下再进行更细致的判断,这时就可以使用if嵌套命令。if嵌套命令允许我们在一个if语句的代码块中再嵌套另一个if语句的代码块,以此类推。 下面是一个详细讲解if嵌套命令的攻略,包含两个示例说明。 示例一:判断一个数的正负和奇偶性 num…

    other 2023年7月27日
    00
  • 魔兽世界6.2冰DK属性选择及输出手法详解

    魔兽世界6.2 冰冷死亡骑士属性选择及输出手法详解攻略 1. 介绍 本篇攻略主要针对魔兽世界6.2版本中,冰冷死亡骑士的属性选择和输出手法进行详细讲解。旨在帮助读者更好地了解该职业的基本操作和优化方法。 2. 属性选择 2.1. 基本属性 在选择属性时,冰冷死亡骑士最重要的属性是力量和全能。力量可以提高伤害输出和技能强度,而全能则可以提高暴击和多重打击。其他…

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