zoukankan      html  css  js  c++  java
  • select 实现server I/O多路复用通信

    • 非阻塞模式下客户端发生异常或服务器未处理完数据,就会立刻返回错误,不用等待

    服务器端

    • 建立连接
    • 设置为非阻塞模式
    import select,queue,socket
    server=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost',2223))
    server.listen(5)
    server.setblocking(False)
    • 使用select进行监测
    inputs=[server,]#一开始要先监测server,知道是否建立连接
    outputs=[]
    
    readable, writable, excetional = select.select(inputs, outputs, inputs)  # select 进行监听
    • 判断最开始inputs里是否是server,是的话表示建立了连接
            if r is server:#表示建立连接
                conn,addr=server.accept()
                conn.setblocking(False)#非阻塞
                inputs.append(conn)#select监测连接过来的实例
                msg_dic[conn]=queue.Queue()#初始化一个队列,用于存放每一个连接的数据
                print('建立连接',addr)
    • 不是代表客户端开始发送数据,服务器接收数据并放入之前初始化的队列中
    data=r.recv(1024)
    print(data.decode())
    msg_dic[r].put(data)#向队列中存入数据
    if r not in outputs:
        outputs.append(r) 
                        
    • 向客户端发送数据,发送完毕从队列中移除连接
     for w in writable:
            try:
                next_msg = msg_dic[w].get_nowait()#从队列中取元素,不等待
            except queue.Empty:
                # print('output queue for', w.getpeername(), 'is empty')
                outputs.remove(w)
            else:
                # print('sending "%s" to %s' % (next_msg, w.getpeername()))
                w.send(next_msg)
    • 异常处理
      for e in excetional:#发生异常就将连接移除
            inputs.remove(e)
            if e in outputs:
                outputs.remove(e)
            e.close()
            del msg_dic[e]#从队列中移除连接

    服务器端完整代码

    #select模拟socketserver
    import select,queue,socket
    server=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost',2223))
    server.listen(5)
    server.setblocking(False)
    inputs=[server,]#一开始要先监测server,知道是否建立连接
    outputs=[]
    msg_dic={}
    while True:
        readable, writable, excetional = select.select(inputs, outputs, inputs)  # select 进行监听
        for r in readable:
            if r is server:#表示建立连接
                conn,addr=server.accept()
                conn.setblocking(False)#非阻塞
                inputs.append(conn)#select监测连接过来的实例
                '''新建立的连接还没发数据过来,现在就接收数据的话是空的,会报错,
                因此要让select监测每一个连接过来的连接,这样才知道客户端发送数据过来'''
                msg_dic[conn]=queue.Queue()#初始化一个队列,用于存放每一个连接的数据
                print('建立连接',addr)
            else:
                try:
                    data=r.recv(1024)#不可以直接判断数据是否为空,因为客户端断开时服务器已经接收不到数据了
                    #有数据发送过来
                    print(data.decode())
                    msg_dic[r].put(data)
                    if r not in outputs:
                        outputs.append(r)  # 放入返回的连接队列里,下一次循环select检测的时候发给客户端
                except ConnectionResetError as e:#处理没有数据的情况,即服务器断开
                    print(addr,' is close...')
                    if r in outputs:#移除连接
                        outputs.remove(r)
                    inputs.remove(r)
                    r.close()
                    del msg_dic[r]
                    break
        for w in writable:
            try:
                next_msg = msg_dic[w].get_nowait()#从队列中取元素,不等待
            except queue.Empty:
                # print('output queue for', w.getpeername(), 'is empty')
                outputs.remove(w)
            else:
                # print('sending "%s" to %s' % (next_msg, w.getpeername()))
                w.send(next_msg)
            # outputs.remove(w)#从outputs中移除旧的连接
        for e in excetional:#发生异常就将连接移除
            inputs.remove(e)
            if e in outputs:
                outputs.remove(e)
            e.close()
            del msg_dic[e]#从队列中移除连接
    View Code

    注:判断客户端是否断开时,要用捕获异常来处理,不能直接判断服务器接收的数据是否为空,因为此时客户端已经断开了,服务器无法接收数据了,会报ConnectionResetError

    客户端完整代码

    import socket
    client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('localhost',2223))
    while True:
        cmd=input(">>>")
        if cmd=='':
            continue
        client.send(cmd.encode())
        data=client.recv(1024)
        print(data.decode())
    View Code
  • 相关阅读:
    需求工程阅读笔记03
    需求工程阅读笔记02
    【Augmented Reality】增强现实中的光学透射式头盔显示器的标定进阶
    基于单个RGB摄像头的手势识别程序设计与实现
    将Vuforia程序发布到Windows10系统的基本流程
    基于Unity3D 的Vuforia SDK开发基础教程
    微软KinectV2深度传感器在Ubuntu上的配置和使用
    Windows 10(64位)配置Caffe运行环境的基本流程
    Ubuntu14.04 64位配置Caffe 教程(基于CUDA7.5)
    空间增强现实——基于贝塞尔曲面的异形表面投影变形技术
  • 原文地址:https://www.cnblogs.com/Aprilnn/p/9318306.html
Copyright © 2011-2022 走看看