zoukankan      html  css  js  c++  java
  • IO模型之非阻塞IO

    1. IO模型非阻塞 IO

    Linux下,可以通过设置socket使其变为 non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:

    从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是用户就可以在本次到下次再发起read询问的时间间隔内做其他事情,或者直接再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存(这一阶段仍然是阻塞的),然后返回。

    也就是说非阻塞的 recvform 系统调用调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,
    此时会返回一个 error 。进程在返回之后,可以干点别的事情,然后再发起 recvform 系统调用。重复上面的过程,循环往复的进行 recvform 系统调用。这个过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。
    

    所以,在非阻塞式 IO 中,用户进程其实是需要不断的主动询问 kernel 数据准备好了没有

    非阻塞 IO 示例

    服务端

    
    import socket
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(("127.0.0.1", 8088))
    server.listen(5)
    server.setblocking(False)
    conn_List = [] # 用来存储接受到的客户端连接 
    
    while True:  # 死循环一直监听
    			try:
    					conn, addr = server.accept()  # 连接循环
    					conn_List.append(conn)  # 收到了就客户端的反应就加到 conn_List 列表里
    					print(conn_List)
    			except BlockingIOError:  # 没收到就会抛出异常
    				conn_del_list = [] 
    				conn_send_list = []# 生成两个列表 一个是需要删除的客户端连接  一个是 接受一个要发送数据的连接 还有需要发送的数据  
    				
    				for conn in conn_List:  # 循环已经加到 conn_List 列表里的客户端连接
    					try:
    						data = conn.recv(1024)  # 接收客户端的消息
    						conn_send_list.append((conn, data))
    						if not data: # 没有收到就说明客户端连接断开 Linux 
    							conn_del_list.append(conn)  # 把没收到的连接放进要删除连接的列表里 注意不能在这里直接remove 因为循环列表过程中改变列表内元素的值 容易造成意外,所以先加到 conn_del_list 里面。
    							continue
    					except BlockingIOError: # 这个recv的动作是一个IO操作 所以 可能会卡主 设置False之后会报错 需要捕获异常 接着循环下一个 知道有客户端反应
    						pass
    
    					except ConnectionResetError:  # window 上的客户端断开异常
    						conn_del_list.append(conn)
    				conn_send_del_list = []  # 定义一个发送完数据给客户端需要删除的列表
    				for conn_send in conn_send_list:  # 循环刚刚 收到消息的列表 以及要发送数据的那个元素元素
    					conn, data = conn_send
    					try:
    						conn.send(data.upper())  # send 可能会卡主 需要捕获异常 
    						conn_send_del_list.append(conn_send)  发完消息的conn 加到要删除的列表里
    
    					except BlockingIOError:
    						continue
    
    				for conn_del in conn_del_list: # 删除刚才再循环中不能删除的conn
    						conn_List.remove(conn_del)
    				for conn_send_del in conn_send_del_list:
    						conn_send_list.remove(conn_send_del)
    
    
    

    客户端:

    
    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 8081))
    
    while True:
        msg = input("msg>>:")
        client.send(msg.encode("utf8"))
        data = client.recv(1024)
    print(data.decode("utf8"))
    
  • 相关阅读:
    权限系统设计-day02
    权限系统设计-day01
    SSH集成(Struts+Spring+Hibernate)
    Spring-day03
    Spring-day02
    神经网络与深度学习
    深度学习概述
    结构化机器学习项目
    无监督学习方法
    《2018自然语言处理研究报告》整理(附报告)
  • 原文地址:https://www.cnblogs.com/zjcode/p/9035506.html
Copyright © 2011-2022 走看看