zoukankan      html  css  js  c++  java
  • socket之IO多路复用

    概述

      目的:同一个线程同时处理多个IO请求。

      本文以python的select模块来实现socket编程中一个server同时处理多个client请求的问题。

      web框架tornado就是以此实现多客户端连接问题的。以下为select源码说明:

    def select(rlist, wlist, xlist, timeout=None): # real signature unknown; restored from __doc__
        """
        select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
        
        Wait until one or more file descriptors are ready for some kind of I/O.
        The first three arguments are sequences of file descriptors to be waited for:
        rlist -- wait until ready for reading
        wlist -- wait until ready for writing
        xlist -- wait for an ``exceptional condition''
        If only one kind of condition is required, pass [] for the other lists.
        A file descriptor is either a socket or file object, or a small integer
        gotten from a fileno() method call on one of those.
        
        The optional 4th argument specifies a timeout in seconds; it may be
        a floating point number to specify fractions of seconds.  If it is absent
        or None, the call will never time out.
        
        The return value is a tuple of three lists corresponding to the first three
        arguments; each contains the subset of the corresponding file descriptors
        that are ready.
        
        *** IMPORTANT NOTICE ***
        On Windows and OpenVMS, only sockets are supported; on Unix, all file
        descriptors can be used.
        """
        pass
    
    # classes

    实例1

    server端

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    import socket
    import select
    ss = socket.socket()
    ss.bind(("localhost",8000))
    ss.listen(5)
    ss.setblocking(False)
    
    inputs = [ss]
    while True:
        rList,wList,e = select.select(inputs,[],[],2)
        print "inputs:", inputs
        print "resaults:", rList
        for r in rList:
            if r == ss:
                con,addr = r.accept()
                inputs.append(con)
            else:
            
            
                try:
                    data = r.recv(1024)
                except socket.error,e:
                    inputs.remove(r)
                else:
                    r.send(data)
    server

    client端

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    import socket
    
    sc = socket.socket()
    sc.connect(("localhost",8000))
    while True:
        data = raw_input("Input:")
        sc.sendall(data)
        print sc.recv(1024)
    sc.close()
    client

    操作步骤

    1. 启动server
    2. 启动client,并输入:123
    3. 再次启动client,并输入:345

    操作结果:

    • client1
    Input:123
    123
    Input:
    • client2
    Input:345
    345
    Input:
    • server端
    #启动server,不启动client,select监听的句柄为:ss_70,无变化,select监听结果:resaults = []
    inputs: [<socket._socketobject object at 0x0241FC70>]
    resaults: []
    #启动client,句柄ss_FC70连接了客户端,select将监听到的变化的句柄返回,rList=[ss_70]
    inputs: [<socket._socketobject object at 0x0241FC70>]
    resaults: [<socket._socketobject object at 0x0241FC70>]
    #将ss.accept()得到的客户端句柄conn_A8追加至监听列表,inputs = [ss_70,conn_A8,],当client不发送请求时,select监听的inputs列表中的句柄没有发生变化,返回列表resaults=[]
    inputs: [<socket._socketobject object at 0x0241FC70>, <socket._socketobject object at 0x0241FCA8>]
    resaults: []
    #client发送信息时,server端select监听的inputs列表中conn_A8句柄发生变化,select返回监听结果:rList = [conn_A8]
    inputs: [<socket._socketobject object at 0x0241FC70>, <socket._socketobject object at 0x0241FCA8>]
    resaults: [<socket._socketobject object at 0x0241FCA8>]
    #在无client连接和没有已连接的客户端发送消息,select所监听的inputs列表无增加,返回列表rList为空
    inputs: [<socket._socketobject object at 0x0241FC70>, <socket._socketobject object at 0x0241FCA8>]
    resaults: []
    #client项server发送消息,select监听列表中客户端句柄conn_A8发生变化,select监听返回结果rList=[conn_A8]
    inputs: [<socket._socketobject object at 0x024DFC70>, <socket._socketobject object at 0x024DFCA8>]
    resaults: [<socket._socketobject object at 0x024DFCA8>]
    
    #接下来就有意思了,保持第一个客户端不断开,再打开第二个客户端client2,句柄ss_70发生变化(我们在服务端只创建了一个socket实例),又会产生一个新的client2的回话句柄conn_E0,追加至select监听列表inputs中
    inputs: [<socket._socketobject object at 0x0241FC70>, <socket._socketobject object at 0x0241FCA8>]
    resaults: [<socket._socketobject object at 0x0241FC70>]
    inputs: [<socket._socketobject object at 0x0241FC70>, <socket._socketobject object at 0x0241FCA8>, <socket._socketobject object at 0x0241FCE0>]
    resaults: []
    #client2向server端发送消息,client2的回话句柄conn_E0发生变化,被返回,rList=[conn_E0]
    inputs: [<socket._socketobject object at 0x0241FC70>, <socket._socketobject object at 0x0241FCA8>, <socket._socketobject object at 0x0241FCE0>]
    resaults: [<socket._socketobject object at 0x0241FCE0>]

    实例2

    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    import socket
    import select
    import Queue
    ss = socket.socket()
    ss.bind(("localhost",8000))
    ss.listen(5)
    #设置为False,accept接受消息时为非阻塞
    ss.setblocking(False)
    #selec监听列表,若列表中哪个句柄发生变化,返回个readAble,否则readAble列表为空
    rList = [ss]
    #写列表,writeAble == wList,二者相等,select返回值即wList的值
    wList = []
    #定义一个字典,key:客户端句柄,value:接收和发送的消息队列;用于收和发之间共享数据
    msg_queues = {}
    while True:
        readAble,writeAble,e = select.select(rList,wList,[],2)
        for r in readAble:
            if r == ss:
                conn,addr = r.accept()
                rList.append(conn)
            else:
                #创建接收的消息队列
                msg_queues[r] = Queue.Queue()
                try:
                    data = r.recv(1024)
                #客户端断开连接会抛出:socket.error 10054异常,将此客户端连接句柄从select监听列表中移除
                except socket.error,e:
                    rList.remove(r)
                else:
                    #将接收到的消息加入句柄所对应的队列中
                    msg_queues[r].put(data)
                    #如果此client句柄wList列表不存在,就加入wList列表
                    if r not in wList:
                        wList.append(r)
        for w in wList:
            try:
                w.sendall(msg_queues[w].get_nowait())
            except Queue.Empty:
                pass
            #因为select监听wList时,只要wList列表中有,就返回wList中所有的句柄,所以使用完后需要删除
            wList.remove(w)
            del msg_queues[w]
    select
  • 相关阅读:
    json数据在前端(javascript)和后端(php)转换
    几个提高效率的PHOTOSHOP秘密快捷键
    移动端ios针对input虚拟键盘挡住的问题
    前端适配移动端的方法
    完美兼容IE10以下所有版本
    vscode vue文件格式化没效果
    官网顶部的标题左移动
    模拟后台一次性返回所有数据
    关于上传图片的问题
    iframe标签在PC端的使用
  • 原文地址:https://www.cnblogs.com/chbo/p/7018455.html
Copyright © 2011-2022 走看看