文档章节

Python Select模型

mickelfeng
 mickelfeng
发布于 2017/05/22 22:33
字数 1120
阅读 9
收藏 0

缘由 
之前写socket的CS模型代码,都是利用最原始的多线程方式。服务端是主线程,接到客户端的连接请求就从线程池中获取一个线程去处理整个socket连接的所有操作,虽然在连接数较短的情况下没有什么影响,但是当连接数很大的情况下,线程的切换和线程池的大小问题就明显起来了。

问题 
应该存在一种方式可以让一个线程去处理多个连接,在连接有事情做的时候才过去处理,不然的话线程就挂起,让线程的利用率更高,于是后来学习了select以及epoll。在这里我重点总结一下select

select的原理 
select是监听触发机制,监听可读和可写队列。当有事件发生时,进行遍历轮询事件发生的对象然后返回 
比如在服务端启动之后把服务端添加到select的可读监听队列中,当有客户端请求连接服务端时,select函数会返回一个可读事件再让服务端接受客户端的连接。 
select的返回方式可以是阻塞或者是非阻塞,非阻塞式的select处理方式是轮询的,会不断询问占用Cpu太多的资源和时间,所以建议使用阻塞等待返回的方式去使用select

优点: 
没有了多线程的创建、销毁、切换带来的效率和内存上的消耗 
缺点 
select存在一个最大可监听文件描述符数量,所以会收到最大连接监听的限制 
select在事件发生以后也是需要遍历监听对象,并不能直接定位到哪个对象,这个操作在对象数量庞大的情况下是个效率的瓶颈。所以后来有了epoll

程序流程描述: 
1. 在创建了server的socket以后,通过无限循环监听select事件执行相应的操作 
2. 客户端请求连接服务端,inputs非空,outputs为空,为queue增添客户端对象 ,readable非空,writeable为空 
3. 服务端接收客户端发送的数据,为outputs增添消息数据,readable不为空,writeable为空,如果接收失败说明客户端断开了连接,则终止监听此客户端的消息,回到第1步等待客户端连接 
4. outputs非空触发select事件,readable为空,writeable不为空,将queue中的数据发送到客户端,如果queue为空触发清除outputs操作 
5. 等待客户端发送数据,回到第2步

过程流程图

服务端代码:

#!/usr/bin/env python
#*-* coding:utf-8 *-*
import select
import socket
import Queue
import time
import os

class Server():
    #创建socket 套接字
    def __init__(self):
        self.server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.server.setblocking(False)
        #配置参数
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_address= ('127.0.0.1',8008)
        self.server.bind(self.server_address)
        self.server.listen(10)
        self.inputs = [self.server]
        self.outputs = []
        self.message_queues = {}
        #timeout = 20

    def run(self):
        while self.inputs:
            print "=================================="
            print "waiting for next event"
            print "inputs",self.inputs
            print "outputs",self.outputs
            print "queue", self.message_queues
        #readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout)  最后一个是超时,当前连接要是超过这个时间的话,就会kill
            readable , writable , exceptional = select.select(self.inputs, self.outputs, self.inputs)
            print "readable , writable , exceptional",readable , writable , exceptional
            # When timeout reached , select return three empty lists
            if not (readable or writable or exceptional) :
                print "Time out ! "
                break;
            for s in readable :
                if s is self.server:
                    #通过self.inputs查看是否有客户端来
                    connection, client_address = s.accept()
                    print "    connection from ", client_address
                    connection.setblocking(0)
                    self.inputs.append(connection)
                    self.message_queues[connection] = Queue.Queue()
                else:
                    try:
                        data = s.recv(1024)
                    except:
                        print "  closing", client_address
                        if s in self.outputs :
                            self.outputs.remove(s)
                        self.inputs.remove(s)
                        s.close()
                        #清除队列信息
                        del self.message_queues[s]
                    else:
                        if data :
                            print " received " , data , "from ",s.getpeername()
                            self.message_queues[s].put(data)
                            # Add output channel for response
                            if s not in self.outputs:
                                self.outputs.append(s)
            for s in writable:
                try:
                    next_msg = self.message_queues[s].get_nowait()
                except Queue.Empty:
                    print " " , s.getpeername() , 'queue empty'
                    self.outputs.remove(s)
                else:
                    print " sending " , next_msg , " to ", s.getpeername()
                    os.popen('sleep 5').read()
                    s.send(next_msg)

            for s in exceptional:
                print " exception condition on ", s.getpeername()
                #stop listening for input on the connection
                self.inputs.remove(s)
                if s in self.outputs:
                    self.outputs.remove(s)
                s.close()
                #清除队列信息
                del self.message_queues[s]

if __name__ == "__main__":
    s = Server()
    s.run()

客户端代码:

# -*- coding:UTF-8 -*-
import socket
import os
import signal
import time, threading
def SendDataToServer(sock):
    while True:
        put = str(raw_input())
        sock.send(put)

def ReceiveData(sock):
    while True:
        try:
            data = sock.recv(1024)
            time.sleep(1)
        except:
            print "Server Down!"
            os._exit(0)
        else:
            if data:
                print "RECEIVE:",data

if __name__ == "__main__":
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('127.0.0.1', 8008))
    send_tick = threading.Thread(target=SendDataToServer, args=(s,))
    rec_tick = threading.Thread(target=ReceiveData, args=(s,))
    send_tick.start()
    rec_tick.start()

感谢以下作者的资料分享: 
select使用详解:http://www.cnblogs.com/coser/archive/2012/01/06/2315216.html 
C解析select:http://blog.csdn.net/piaojun_pj/article/details/5991968

以上为本人学习总结经验,如有错误欢迎评论斧正,谢谢~

本文转载自:http://blog.csdn.net/dw_deven/article/details/52944395

mickelfeng

mickelfeng

粉丝 234
博文 2769
码字总数 597345
作品 0
成都
高级程序员
私信 提问
goless —— Go 风格的 Python 并发编程库

使用 goless 库,你可以用 Python 语言编写 Go 语言风格的并发程序。goless 提供了 channels、select 和 gooutines 的函数,允许你使用 Go 语言漂亮和优雅的并发编程模型,但是以你习惯的 Py...

oschina
2014/08/18
64
0
[SQL Server玩转Python] 三.SQL Server存储过程实现Python鸢尾花决策树训练及预测

版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!共同学习,一起进步~ https://blog.csdn.net/Eastmount/article/details/84066650 在开发项目过程中,更多的是通过Python访问SQL...

Eastmount
2018/11/14
0
0
【转】Django 数据库的操作

class Publisher(models.Model): VALUES WHERE id = 52; FROM book_publisher; 相当于SQL语句:SELECT FROM book_publisherWHERE name = 'NewName'; 缩小范围:Publisher.objects.filter(co......

寂寞的远行者
2012/08/02
0
0
Python中使用epoll开发服务端程序

服务端代码: 客户端代码: 这是个很简单的C/S模型的程序,流程其实和C语言相差不大,就是为了学学python中的基本语法,以及logging,select,socket模块的使用。客户端发送字符串,服务端再将该...

鉴客
2010/06/04
3K
2
适合初学者的 Python IDE - Thonny

Thonny —— 一个面向初学者的 Python IDE Thonny 由爱沙尼亚的 Tartu 大学开发,它采用了不同的方法,因为它的调试器是专为学习和教学编程而设计的。 特性 易于上手。Thonny 内置了 Python...

匿名
2018/10/23
0
8

没有更多内容

加载失败,请刷新页面

加载更多

Angular 英雄编辑器

应用程序现在有了基本的标题。 接下来你要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。 创建英雄组件 使用 Angular CLI 创建一个名为 heroes 的新组件。 ng gener...

honeymoose
13分钟前
1
0
Kernel DMA

为什么会有DMA(直接内存访问)?我们知道通常情况下,内存数据跟外设之间的通信是通过cpu来传递的。cpu运行io指令将数据从内存拷贝到外设的io端口,或者从外设的io端口拷贝到内存。由于外设...

yepanl
今天
4
0
hive

一、hive的定义: Hive是一个SQL解析引擎,将SQL语句转译成MR Job,然后再在Hadoop平台上运行,达到快速开发的目的 Hive中的表是纯逻辑表,就只是表的定义,即表的元数据。本质就是Hadoop的目...

霉男纸
今天
3
0
二、Spring Cloud—Eureka(Greenwich.SR1)

注:本系列文章所用工具及版本如下:开发工具(IDEA 2018.3.5),Spring Boot(2.1.3.RELEASE),Spring Cloud(Greenwich.SR1),Maven(3.6.0),JDK(1.8) Eureka: Eureka是Netflix开发...

倪伟伟
昨天
9
0
eclipse常用插件

amaterasUML https://takezoe.github.io/amateras-update-site/ https://github.com/takezoe/amateras-modeler modelGoon https://www.cnblogs.com/aademeng/articles/6890266.html......

大头鬼_yc
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部