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

相关文章

  • iframe 多层嵌套 无限嵌套 高度自适应的解决方案

    iframe 多层嵌套 无限嵌套 高度自适应的解决方案攻略 在处理 iframe 多层嵌套、无限嵌套以及高度自适应的问题时,我们可以采用以下解决方案。 1. 使用 JavaScript 跨域通信 为了实现 iframe 的高度自适应,我们需要在父级页面和子级页面之间进行跨域通信。以下是一个示例: 父级页面代码 <!DOCTYPE html> &l…

    other 2023年7月28日
    00
  • C语言中动态内存分配malloc、calloc和realloc函数解析

    C语言中动态内存分配函数解析 在C语言中,动态内存分配是一种重要的技术,它允许程序在运行时动态地分配和释放内存。C语言提供了几个函数来实现动态内存分配,其中包括malloc、calloc和realloc函数。本文将详细解析这三个函数的用法和区别。 1. malloc函数 malloc函数用于在堆上分配指定大小的内存块。它的函数原型如下: void* mall…

    other 2023年8月2日
    00
  • 电脑如何设置静态ip?如何设置静态IP进行专线上网?

    电脑如何设置静态IP? 要设置电脑的静态IP,您可以按照以下步骤进行操作: 打开控制面板:点击开始菜单,然后选择“控制面板”。 进入网络和Internet设置:在控制面板中,找到并点击“网络和Internet”。 打开网络和共享中心:在网络和Internet设置页面中,点击“网络和共享中心”。 打开适配器设置:在网络和共享中心页面中,点击左侧的“更改适配器设…

    other 2023年7月29日
    00
  • css控制元素上下左右居中

    CSS控制元素上下左右居中 在前端开发中,控制元素在页面中居中是一个经常被提及的问题。本文将介绍几种使用CSS控制元素上下左右居中的方法。 1. 使用flex布局 现代CSS有很多可以将元素居中的方法,其中使用flexbox布局是应用最为广泛的方式之一。在使用前,请确保目标元素的父元素被设置为 display: flex。 .parent { display…

    其他 2023年3月29日
    00
  • IOS开发使用KeychainItemWrapper 持久存储用户名和密码

    IOS开发使用KeychainItemWrapper 持久存储用户名和密码 在 IOS 应用中,要求用户输入账户和密码以进行身份验证是很普遍的需求。但是,为了确保用户输入的凭证不会丢失或者被黑客攻击,我们需要在应用程序中使用安全的方式来存储这些凭证。KeychainItemWrapper 可以帮忙实现安全地存储这些凭证,下面详细介绍一下。 步骤 下载 Key…

    other 2023年6月27日
    00
  • 解析Java继承中方法的覆盖和重载

    下面是详细讲解“解析Java继承中方法的覆盖和重载”的完整攻略。 什么是Java继承? Java继承是一种面向对象编程的重要概念。在Java中,子类可以从父类继承属性和方法,从而减少代码的重复,提高代码的复用性。子类也可以新增自己特有的属性和方法。通过继承,子类可以使用父类的方法和属性,同时也可以根据自身需要进行扩展和修改。在Java中,子类可以覆盖或重载父…

    other 2023年6月27日
    00
  • Lua在各个操作系统中的开发环境配置教程

    Lua在各个操作系统中的开发环境配置教程 Windows操作系统 在官网下载最新版的Lua安装包。 打开安装包,按照安装向导的提示进行安装。选择安装路径时,最好将路径添加到系统环境变量中,这样才能在任意文件夹中使用Lua命令。 安装完成后,在命令提示符中输入lua -v,如果提示Lua的版本信息,则证明安装成功。 推荐使用ZeroBrane Studio作为…

    other 2023年6月26日
    00
  • Python如何使用type()函数查看数据的类型

    Python如何使用type()函数查看数据的类型攻略 在Python中,可以使用type()函数来查看数据的类型。以下是使用type()函数查看数据类型的详细攻略: 使用type()函数查看基本数据类型的示例: num = 10 print(type(num)) # 输出:<class ‘int’> name = \"John\&qu…

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