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
  • 相关阅读:
    Collections接口下的Comparetor类和Comparable接口排序
    MySQL(二)、数据库数据类型
    MySQL(表设置、详细说明查询操作)、JDBC基础
    IO流:对象流、Poperties类
    IO流:System.in、SequenceInputStream合并流、内存输入输出流、数据流
    IO流:文件对象、字符流、字节流、缓冲流
    泛型
    集合
    String常用方法、toString()、equals()、compareTo()
    继承、抽象、接口、封装
  • 原文地址:https://www.cnblogs.com/Aprilnn/p/9318306.html
Copyright © 2011-2022 走看看