zoukankan      html  css  js  c++  java
  • Python select实现socket并发

    Python select 
      Python的select()方法直接调用操作系统的IO接口,它监控sockets,open files, and pipes(所有带fileno()方法的文件句柄)何时变成readable 和writeable, 或者通信错误,select()使得同时监控多个连接变的简单,并且这比写一个长循环来等待和监控多客户端连接要高效,因为select直接通过操作系统提供的C的网络接口进行操作,而不是通过Python的解释器。
    注:使用Python的文件对象选择()为Unix工作,但在Windows下不支持。
     
    select实现socketserver
    注:接下来通过echo server例子要以了解select 是如何通过单进程实现同时处理多个非阻塞的socket连接的
    注:必须在非阻塞情况下。
     
    服务端:
    import select
    import socket
    import queue
    
    server = socket.socket()
    server.bind(('localhost' ,9000))
    server.listen(1000)
    
    # 设置非阻塞模式
    server.setblocking(False)
    msg_dic = {}
    
    # 内核检测并发链接
    inputs = [server,]
    
    # outputs存放链接需要返回的数据
    outputs = []
    
    # 为了循环调入select加入while
    while True:
    
        # 1,需要内核检测哪些链接,有一个活动就返回所有链接循环。
        # 2,处理返回数据。
        # 3,如果有并发的链接断开,内核会返回报错到inputs内,有哪几个有问题。
        # 有链接进入会返回三个数据:
        # readable:返回一个列表,活动的,可读数据的
        # writeable:存放需要返回的数据。
        # exceptional:返回出现异常的活动链接
        readable,writeables,exceptional = select.select(inputs,outputs,inputs)
        print(readable,writeables,exceptional)
    
        # 收处理
        for r in readable:
            # 代表来了一个新连接
            if r is server:
    
                # 等待客户端生成实例
                conn,addr = server.accept()
    
                # 打印客户端IP地址
                print("来了个新连接",addr)
    
                #   因为这个新建里的链接还没发数据过来,现在就收数据的话程序就报错了
                #所以要想实现客户端发数据来时server端能知道,就需要让select在检测这
                #个conn。
                inputs.append(conn)
    
                # 初始化一个队列,后面存要返回这个客户端的数据
                msg_dic[conn] = queue.Queue()
    
            # 接收新连接数据
            else:
                # 获取数据
                data = r.recv(1024)
                # 打印数据
                print("收到数据:",data)
    
                # 将返回的数据排列到队列中
                msg_dic[r].put(data)
    
                # 放入返回的链接队列
                outputs.append(r)
    
        # 发数据:要返回给客户端的链接列表
        for w in writeables:
    # 重链接列表中取出队列的实例 data_to_client = msg_dic[w].get()
    # 返回给客户端数据 w.send(data_to_client)
    # 确保下次循环的时候writeable,不返回这个已经处理完的链接 outputs.remove(w) # 删除:错误链接 for e in exceptional:
    # 查找错误链接是否存在outputs if e in outputs:
    # 如果有就删除错误链接 outputs.remove(e)
    # 删除inputs下的错误链接 inputs.remove(e)
    # 删除队列中的错误链接 del msg_dic[e]

    客户端:

    import socket
    client = socket.socket()
    #client.connect(('192.168.1.177',9999))
    client.connect(('localhost',9000))
    while True:
        msg = input(">>:").strip()
        if len(msg) == 0:continue
        client.send(msg.encode("utf-8"))
        data = client.recv(1024)
        print("recv:",data.decode())
    client.close()
    select实现socketserver并发(完整版)
    import select
    import socket
    import sys
    import queue
    
    
    server = socket.socket()
    server.setblocking(0)
    
    server_addr = ('localhost',10000)
    
    print('starting up on %s port %s' % server_addr)
    server.bind(server_addr)
    
    server.listen(5)
    
    
    inputs = [server, ] #自己也要监测呀,因为server本身也是个fd
    outputs = []
    
    message_queues = {}
    
    while True:
        print("waiting for next event...")
    
        readable, writeable, exeptional = select.select(inputs,outputs,inputs) #如果没有任何fd就绪,那程序就会一直阻塞在这里
    
        for s in readable: #每个s就是一个socket
    
            if s is server: #别忘记,上面我们server自己也当做一个fd放在了inputs列表里,传给了select,如果这个s是server,代表server这个fd就绪了,
                #就是有活动了, 什么情况下它才有活动? 当然 是有新连接进来的时候 呀
                #新连接进来了,接受这个连接
                conn, client_addr = s.accept()
                print("new connection from",client_addr)
                conn.setblocking(0)
                inputs.append(conn) #为了不阻塞整个程序,我们不会立刻在这里开始接收客户端发来的数据, 把它放到inputs里, 下一次loop时,这个新连接
                #就会被交给select去监听,如果这个连接的客户端发来了数据 ,那这个连接的fd在server端就会变成就续的,select就会把这个连接返回,返回到
                #readable 列表里,然后你就可以loop readable列表,取出这个连接,开始接收数据了, 下面就是这么干 的
    
                message_queues[conn] = queue.Queue() #接收到客户端的数据后,不立刻返回 ,暂存在队列里,以后发送
    
            else: #s不是server的话,那就只能是一个 与客户端建立的连接的fd了
                #客户端的数据过来了,在这接收
                data = s.recv(1024)
                if data:
                    print("收到来自[%s]的数据:" % s.getpeername()[0], data)
                    message_queues[s].put(data) #收到的数据先放到queue里,一会返回给客户端
                    if s not  in outputs:
                        outputs.append(s) #为了不影响处理与其它客户端的连接 , 这里不立刻返回数据给客户端
    
    
                else:#如果收不到data代表什么呢? 代表客户端断开了呀
                    print("客户端断开了",s)
    
                    if s in outputs:
                        outputs.remove(s) #清理已断开的连接
    
                    inputs.remove(s) #清理已断开的连接
    
                    del message_queues[s] ##清理已断开的连接
    
    
        for s in writeable:
            try :
                next_msg = message_queues[s].get_nowait()
    
            except queue.Empty:
                print("client [%s]" %s.getpeername()[0], "queue is empty..")
                outputs.remove(s)
    
            else:
                print("sending msg to [%s]"%s.getpeername()[0], next_msg)
                s.send(next_msg.upper())
    
    
        for s in exeptional:
            print("handling exception for ",s.getpeername())
            inputs.remove(s)
            if s in outputs:
                outputs.remove(s)
            s.close()
    
            del message_queues[s]
  • 相关阅读:
    DBG
    gdb Debugging Full Example
    Java Warmup
    Dtrace for Linux 2016
    分布式系统理论进阶
    Java theory and practice
    Dealing with InterruptedException
    JVM 虚拟化
    Intro to Filtering with Network Monitor 3.0
    spring 官方文档
  • 原文地址:https://www.cnblogs.com/xiangsikai/p/8215095.html
Copyright © 2011-2022 走看看