Python处理多个客户端连接---多路复用选择服务器

原创 2016年05月22日 22:32:17

多路复用

到目前为止,我们已经看到如何用分支进程和派生线程来同时处理多个客户端,以及一个封装了这两个方案的库类。在这两种方法下,所有的客户端处理程序似乎都是彼此并行运行(即在同一时间内)运行的,所以在接受新的请求或处理长期运行的客户端处理程序时,服务器未被阻塞。

不过从技术上讲,线程和进程并不是真正并行运行的,除非你足够幸运,机器有多个cpu。相反,你的操作系统可以执行一个变戏法的操作—它在所有活动任务之间分配计算机的处理能力。它先运行其中的某个任务的一部分,然后再运行另一个任务的一部分,就这样继续下去。所有任务看上去好像是并行运行的,但只是因为操作系统在任务切换的焦点如此之快,以至于你通常注意不到。这个由操作系统完成,在任务之间切换的进程有时也被称为时间片,在更多情况下,它被称为多路复用。

当我们派生线程和进程时,我们依靠操作系统来控制运行状态的任务,所以没有任务急需计算资源,尤其是主服务器调度循环。然而,没有理由可以任务python脚本不能这么做。例如一个脚本可能把任务分成多个步骤,先运行一个任务的某个步骤,然后再运行另一个任务的某个步骤,如此继续下去,直到所有的任务都完成。改脚本只需知道如何把注意力划分到多个活动任务中,以便在其自身采用多路复用。

select

服务器可以应用这种技术产生的另一种方式来一次处理多个客户端。改方式既不需要线程也不需要分支。通过多路复用客户端连接和拥有select系统调用的主调用程序,一个单一的事件循环就可以处理多个客户端,并可以并行接受新的客户端。这样的服务器有时也称为异步,在异步服务器上,一个单一的主循环在一个单独的进程和线程中运行,决定每次哪些客户端应该得到关注。如果已经准备好回话,那么客户端请求和主调用循环都会获得服务器小片的注意。

该服务器背后是操作系统select调用,在所有主要的平台上的python标准select模块中都可以获得。select让我们把注意力直接放到可以随时回话的套接字上,以避免阻塞对那些没有准备好的套接字的调用。也就是说,当select是套接字,我们可以确定套接字调用,如accept,recv和send通过select应用到返回对象时,不会阻塞服务器。正因为如此,使用select的单循环服务器不需要暂停与某个客户端的会话或等待新的客户端,而其他客户端则在焦急等待服务器的关注。

因为这种类型的服务器不需要启动线程或者进程,所以当与客户端的事务相对短暂时,这种类型的服务器可以发挥很大的作用。然而,它也要求这些事务是快速的;如果这些事务不够快,在等待某个特定时客户端会话结束时,它仍然有阻塞的风险,除非增加长时间运行会话的线程或者分支。

基于select的响应服务器

//服务器:使用select并行处理多个客户端。使用select模块手动在套接字之间多重通道传输:接收新的客户端连接的主套接字,并输入套接字到接收的客户端;select可以采用可选的第四个参数0来轮询,用n.m等待n.m秒,或者忽略等待直到任意套接字准备好了,可以处理
__author__ = 'JianqingJiang'
# -*- coding: utf-8 -*-
import sys,time
from socket import select
from socket import socket,AF_INET,SOCK_STREAM
def now: return time.ctime(time.time())
myHost = ''                                                # server machine, '' means local host
myPort = 50007                                             # listen on a non-reserved port number
if len(sys.argv) == 3:                                     # allow host/port as cmdline args too
    myHost,myPort = sys.argv[1:]
numPortSocks = 2                                           # number of ports for client connects

# make main sockets for accepting new client requests

mainsocks,readsocks,writesocks = [],[],[]
for i in range(numPortSocks):
    portsock=socket(AF_INET,SOCK_STREAM)                   # make a TCP/IP socket object
    portsock.bind((myHost,myPort))                         # bind it to server port number
    portsock.listen(5)                                     # listen,allow 5 pending connects
    mainsocks.append(portsock)                             # add to main list to identify
    readsocks.append(portsock)                             # bind on consecutive ports
    myPort += 1

print('select-server loop starting')
while True:
    #print(readsocks)
    readable,writeables,exceptions = select(readsocks,writesocks,[])
    for sockobj in readable:
        if sockobj in mainsocks:                           # for ready input sockets
            #port socket:accept new client
            newsock,address = sockobj.accept()             # accept should not block
            print('Connect:',address,id(sockobj))          # newsock is a new socket
            readsocks.append(newsock)                      # add to select list,wait
        else:
            #client socket: read next line
            data=sockobj.recv(1024)                        # recv should not block
            print('\tgot',data,'on',id(sockobj))
            if not data:                                   # if closed by the clients
                data=sockobj.close()                       # close here and remv from
                readsocks.remove(sockobj)                  # del list else reselected
            else:
                #this may block:should really select for writes too
                reply = 'Echo=>%s at %s' % (data,now())
                sockobj.send(reply.encode())

这个脚本的大部分是其最后的while循环,用于调用select,找出哪些套接字已经准备好处理;这些包括两个主要的端口套接字,在这两个套接字上,客户端可以连接并打开客户端连接,然后遍历所有这些准备好的套接字,在主要的端口套接字上接受连接,在任何准备好输入的客户端套接字上读取和响应输入。此代码中的accept和recv调用,在select返回之后,可以保证不阻塞服务器进程;结果,该服务器可以迅速回到循环顶部,处理新来的客户端请求和已连接的客户端输入。其效果是,以伪并行形式服务所有新的请求客户端。

select调用细节

从形式上看,select可以调用可选择对象的三种列表(输入源,输出源和特殊条件源),加一个可选的timeout。timeout参数可能是真正几秒内的等待期限(使用浮点数来表示秒的小数部分),0值意味着简单轮询,并立即返回,或省略意味着等待,直到至少有一个对象准备好。调用返回三个准备阿訇的对象—前三个参数的子集—如果在源准备好之前,timeout超时,任何或所有的对象可能都是空的

select的可移植性

类似线程,但不像分支。从技术上讲select调用仅适用于windows上的套接字,但适用于unix和macintosh上的文件和管道,当然对于在互联网上运行的服务器,我们感兴趣的主要设备是套接字。

非阻塞状态的套接字

select可以让我们肯定,套接字调用(如accept和recv)讲不会阻塞调用者,但是它一般也可能使python套接字处于非阻塞状态。调用套接字对象的setblocking方法把套接字设置成阻塞或非阻塞模式例如,假定一个像sock,setblocking(flag)的调用,如果改flag(标示)为0,套接字sock设置为非阻塞模式,否则设置为阻塞模式。所有套接字最初在阻塞模式下启动,因此套接字调用可能总是让调用者等待。
然而,当在非阻塞模式时,如果某个recv套接字调用没有找到任何数据,或某个send调用不能立即传递数据,就会引起一个socket.error异常。脚本可以捕获这个异常,以确定套接字是否准备好进行处理了。在阻塞模式下,这些调用一直处于阻塞状态,直到它们可以继续下去。当然,有可能存在比数据传输更多的方法,可以处理客户端请求,所以非阻塞的套接字一般不能保证服务器停滞。它们仅仅是另一种多路复用服务器的方式。和select一样,在客户端请求可以迅速等到服务时,它们更合适。

asyncore模块框架

如果你乐于使用select,可能也会对检查python标准库中的asyncore.py模块感兴趣。它实现了一种基于类的回调模型,其通过预编码的select事件循环调度,输入和输出回调到类方法。因此,它可以在没有线程或分支的情况下建立服务器,这就是一个基于select的,可选择的socketserver模块的线程和分支模块,对于一般的服务器类型来说,在处理过程较为简单时,asyncore是最好的—它所描述的是”I/O密集型”,而不是”CPU计算密集型”的程序,其中的后者仍然需要线程或者分支。

Twisted

对于其他服务器选项,也可以参阅开源的Twisted系统,Twisted是用Python编写的,是一个支持tcp,udp,组播,ssl/tsl,串行通信及更多的异步网络框架。它同时支持客户端和服务器,包括一些常用的网络服务的实现,如网络服务器,irc聊天服务器,邮件服务器,关系数据库接口和对象处理。

总结:选择一个服务器计划

那么,你何时应该使用select代替线程或者分支,去建立一个服务器呢?每个应用程序有不同的需求,当然,正如前面所提到的,当客户端处理过程相对来说比较简单,或者不是CPU密集型时,基于select调用服务器一般都表现非常好。如果它们不是简单的处理方法,那么线程或者分支用于传递数据的套接字调用之上或者之外,长期运行处理过程,线程和分支就特别有用,然而,组合使用也是有可能的

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

socket实现多个客户端连接在一个服务器上

1、使用socekt通信一般步骤     1)服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()等待客户端连接。     2)客户端:socker()...

python socket(三)forking 实现网络并发

在python socket(一)和python socket(二)中服务器都只能一个客户端连接。 我们现在要让服务器实现多个连接。 连接都是从conn,addr = s.accept()开始的,如果...

python socket(二)接收多个消息

在python socket(一)中,客户端每次只能发送一次消息,然后连接就断开。 把客户端修改一下,加入一个循环就能让它发送多个消息和接收。 server端代码: # -*- coding: utf...

python 多线程实现多客户端连接的 TCP Server

python 多线程实现多客户端连接的 TCP Server最近看《python核心编程》,书中实现了一个简单的1对1的TCPserver,但是在实际使用中1对1的形势明显是不行的,所以研究了一下如何...

用python的twisted做个简单游戏服务器原形--客户端连接monitor管理类

''' Created on 2012-8-14 @author: qs ''' #from twisted.internet import epollreactor #epollreactor.i...

twisted文档翻译之 概述

1、内容提要     这个文档库中其他任何文档都致力于说明twisted是什么,在这里将试图去说明没有什么是twisted,但是它到底是什么呢,直到我通过它看到了自己的目标。  首先 twisted应...

基于TCP客户端和服务器的I/O多路复用

一、基于TCP/IP协议的基本循环服务器 tcp_server.c [cpp] view plain copy   #include    #...
  • zscfa
  • zscfa
  • 2016年12月05日 00:09
  • 385

socket编程:多路复用I/O服务端客户端之select

其实在之前的TCP之中,我们编程实现了多进程,多线程机制下的TCP服务器,但是对于这种的TCP服务器而言,存在太大的资源局限性。所以我们可以是用I/0模型中的多路复用I/O模型来进行编程。他的具体思想...

使用poll实现的io多路复用服务端和客户端

使用poll实现的io多路复用服务端和客户端。 客户端通过子进程创建多个客户端连接。 客户端每隔1秒向服务端发送一个时间戳, 服务端接收到时间戳以后,保存在本地的文件中, 一个客户端对应一个存储文件,...

socket编程:多路复用I/O服务端客户端之poll

一. 关于poll    对于IO复用模型,其优点无疑是免去了对一个个IO事件就绪的等待,转而代之的是同时对多个IO数据的检测,当检测等待的事件中至少有一个就绪的时候,就会返回告诉用户进程“已经有数据...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Python处理多个客户端连接---多路复用选择服务器
举报原因:
原因补充:

(最多只允许输入30个字)