首先,我们要明确2个问题:

普通套接字实现的服务端有什么缺陷吗?

有,一次只能服务一个客户端!

这种缺陷是如何造成的?

accept阻塞:当没有套接字连接请求过来的时候会一直等待着

recv阻塞:当连接的这个客户端没有发数据过来的时候,也会一直等待着

import socketserver = socket.socket()
server.bind(('127.0.0.1', 8888))
server.listen(5)
print("执行到这, 上面没问题了")while True:conn, addr = server.accept()   # 阻塞print(conn, addr)print("{}连接".format(addr))while True:data = conn.recv(1024)       # 阻塞print(data)if not data:break

当前I/O流

那么非阻塞套接字和普通套接字的区别?

非阻塞套接字在accept或recv的时候不会发生阻塞,要么成功,要么失败抛出BlockingIOError异常

非阻塞IO模型

非阻塞套接字实现并发

并发是什么?

在一个时间段,完成某件事,就是并发

对立的概念,什么是并行?

同时发生,不管有几件事,同时进行,就是并行

非阻塞套接字如何实现并发服务端?

配合try语句,将代码顺序重排,避开阻塞

实现并发服务多个客户端 !

那么现在用非阻塞套接字完善上一章博客的代码:

服务端:

import socket
import time
# 并发操作

server = socket.socket()     # 创建一个socket
server.setblocking(False)    # 设置成非阻塞
server.bind(('0.0.0.0', 8888))
server.listen()
print("执行到这, 上面没问题了")all_connction = []  # 用来存放和连接客户端通信的套接字
while True:# 处理用户的连接try:conn, addr = server.accept()   # 阻塞conn.setblocking(False)    # 设置成非阻塞print(conn, addr)print("{}连接".format(addr))all_connction.append(conn)except BlockingIOError as e:pass# 处理已经连接的客户的消息time.sleep(1)new_li = all_connction.copy()for conn in new_li:try:data = conn.recv(1024)       # 阻塞if data == b'':all_connction.remove(conn)conn.close()else:print("接收到的数据: ", data.decode())conn.send(data)except BlockingIOError as e:passserver.close()

客户端:

#客户端Linux、window系统下:输入命令通过服务端返回
import  socket#声明协议类型,同时生成socket连接对象
client = socket.socket()#链接地址和端口,元组(本地,端口)
client.connect(('127.0.0.1', 8888))#使用input循环向服务端发送请求
while True:msg = input("-->>:").strip()if len(msg) == 0:continue#发送数据 b将字符串转为bys类型client.send(msg.encode("utf-8"))#接收服务器端的返回,需要声明收多少,默认1024字节id = 1024data = client.recv(id).decode()#打印data是recv的dataprint("recv: %s" % data)#关闭接口
client.close()

IO多路复用

 IO多路复用也是阻塞IO, 只是阻塞的方法是select/poll/epoll, 好处就是单个进程可以处理多个socket

用select,poll,epoll监听多个io对象,当io对象有变化(有数据)的时候,则立即通知相应程序进行读或者写操作。

但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写时间就绪后自己负责进行读写,也就是说这个读写过程是阻塞的

因为阻塞I/O只能阻塞一个I/O操作,而I/O复用模型能够阻塞多个I/O操作, 所以才叫做多路复用

非阻塞套接字实现的服务端还有什么不完美的地方吗?

关键一: 任何操作都是要花CPU资源的!

关键二: 如果数据还没有到达。那么accept, recv操作都是在做无用功!

关键三: 对异常BlockIOError的处理也是在做无用功!

总结:不完美的CPU利用率

I/O多路复用模型

 epoll  目前Linux上效率最高的IO多路复用技术!

epoll 基于惰性的事件回调机制

惰性的事件回调是由用户自己调用的,操作系统只起到通知的作用

使用步骤

    导入IO多路复用选择器

  注册事件和回调

  查询

     回调

应用实例:

import selectors  # 提供IO多路技术给我们使用的
import socket
import timeserver = socket.socket()         #创建一个套接字
server.bind(('0.0.0.0', 8888))
server.listen()
epoll = selectors.EpollSelector()  # 生成一个epolldef newFunc(conn):data = conn.recv(1024)if data == b'':epoll.unregister(conn)  # 取消注册
        conn.colse()else:print("收到的数据: ",data.decode())conn.send(data)def func(ser):conn, addr = ser.accept()print("处理了连接")# 查看是否有数据
    epoll.register(conn, selectors.EVENT_READ, newFunc)time.sleep(1)
epoll.register(server, selectors.EVENT_READ, func)    # 注册 给他3个参数   1.想看什么,看的是server   2.有没有数据连接(EVENT_READ可读,EVENT_WRITE可写)  3.回调函数while True:events = epoll.select()  # 查询for key, mask in events:callback = key.data  # 函数的名字sock = key.fileobj  # 套接字
callback(sock)    # 函数调用

import  socketclient = socket.socket()
client.connect(('127.0.0.1', 8888))while True:data = input("请输入要发送的数据:")client.send(data.encode())print("接收到的数据:", client.recv(1024).decode())

下面是一些理论的东西,大家有时间可以读一遍。

## 什么是IO操作

​ IO在计算机中指Input/Output,也就是输入和输出 。由于程序和运行时数据是放在内存中的,由CPU来执行的,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。

​ 比如你打开浏览器,访问百度,浏览器需要通过网络获取百度的网页数据。浏览器首先发送数据给百度的服务器,告诉它我要访问它,这个过程是往外发数据,就做Output。然后百度服务器在把网页数据发过来。这个过程是从外面接收数据,就做input

​ 所以,通常,程序完成IO操作会有Input和Output 这两个过程, 但是也可能只有一个,比如打开一个文件。就只是从磁盘读取文件到内存,就只有Input操作 ,反过来,向文件中写入数据,就只是一个Output操作。

## 1 流的概念

​ IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和百度服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。

​ 由于CPU和内存的速度远远高于外设的速度,所以,在IO编程中,就存在速度严重不匹配的问题。 可能存在这样的情况:读取数据的时候,流中还没有数据;写入数据的时候,流中数据已经满了,没有空间写入了。

​ 举个例子,socket通信, 通过recv读取另一方发过来的数据,但是对方还没把数据准备好发过来。此时有两种处理办法:

- 阻塞,等待数据准备好了,再读取出来返回;
- 非阻塞,通过轮询的方式,查询是否有数据可以读取,直到把数据读取返回。

## 2. 同步,异步,阻塞, 非阻塞的概念

在IO操作过程中,可能会涉及到同步(synchronous)、异步(asynchronous)、阻塞(blocking)、非阻塞(non-blocking)、IO多路复用(IO multiplexing)等概念。他们之间的区别是什么呢?

以socket为例子,在socket通信过程中,涉及到两个对象:

1. 调用这个IO的进程(process)或线程(thread)
2. 操作系统内核(kernel)

比如服务端调用recv来接收客户端的数据,会涉及两个过程:

1. 等待数据准备好(Waiting for the data to be ready),也就是客户端要通过网络把数据发给服务端;
2. 客户端把数据发送过来,首先会被操作系统内核接收到,程序里面需要使用这个数据,要将数据从内核中拷贝到进程中( Copying the data from the kernel to the process))

根据这两个阶段中,不同阶段是否发生阻塞,将产生不同的效果。

### 阻塞 VS 非阻塞

阻塞IO:

- 在1、2阶段都发生阻塞;
- 调用阻塞IO会一直block住进程,直到操作完成

非阻塞IO:

- 在第1阶段没有阻塞,在第2阶段发生阻塞;
- 当用户进程发出IO请求时, 如果内核中的数据还没由准备好,那么它并不会block用户进程,而是立即返回一个错误, 在程序看来,它发起一个请求后,并不需要等待,而是马上就得到一个结果。
- 非阻塞IO需要不断轮询,查看数据是否已经准备好了;

阻塞与非阻塞可以简单理解为调用一个IO操作能不能立即得到返回应答,如果不能立即获得返回,需要等待,那就阻塞了;否则就可以理解为非阻塞 。

### **同步VS异步**

同步与异步是针对应用程序与内核的交互而言的

同步:第二步数据从内核缓存写入用户缓存一定是由用户线程自行读取数据,处理数据。

异步:第二步数据是内核写入的,并放在了用户线程指定的缓存区,写入完毕后通知用户线程。

​ 同步和异步针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。

​ 同步有阻塞和非阻塞之分,异步没有,它一定是非阻塞的。

​ 阻塞、非阻塞、多路IO复用,都是同步IO,异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。真正的异步IO需要CPU的深度参与。换句话说,只有用户线程在操作IO的时候根本不去考虑IO的执行全部都交给CPU去完成,而自己只等待一个完成信号的时候,才是真正的异步IO。所以,拉一个子线程去轮询、去死循环,或者使用select、poll、epool,都不是异步。

​ 同步:执行一个操作之后,进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成,等待结果,然后才继续执行后续的操作。

​ 异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。

​ 阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。

​ 非阻塞:进程给CPU传达任务后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。

## 3. IO多路复用

​ I/O多路复用也是阻塞IO,只是阻塞的方法是select/poll/epoll ,好处就是单个进程可以处理多个socket

用select, poll, epoll监听多个io对象,当io对象有变化(有数据)的时候,则立即通知相应程序进行读或写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的

因为阻塞I/O只能阻塞一个I/O操作,而I/O复用模型能够阻塞多个I/O操作,所以才叫做多路复用。

到这里整理完毕。

作者:含笑半步颠√

博客链接:https://www.cnblogs.com/lixy-88428977

声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,欢迎转载。转载与引用请注明作者及出处。

转载于:https://www.cnblogs.com/lixy-88428977/p/9638949.html

python_非阻塞套接字及I/O流相关推荐

  1. python非阻塞输入_python_非阻塞套接字及I/O流

    首先,我们要明确2个问题: 普通套接字实现的服务端有什么缺陷吗? 有,一次只能服务一个客户端! 这种缺陷是如何造成的? accept阻塞:当没有套接字连接请求过来的时候会一直等待着 recv阻塞:当连 ...

  2. 《Windows网络编程案例教程》-董相志 学习记录 阻塞/非阻塞套接字编程

    <Windows网络编程案例教程>-董相志 学习记录 阻塞/非阻塞套接字编程 2.3 阻塞/非阻塞套接字编程 阻塞套接字编程通信流程图 2.3.1 阻塞套接字客户机编程 1.启动并初始化W ...

  3. 【网络编程】Socket套接字;UDP数据报套接字编程;TCP流套接字编程

    文章目录 1. 什么是网络编程 2. 网络编程中的基本概念 3. Socket套接字 4 UDP数据报套接字编程 4.1 客户端服务器交互流程 4.2 UDP版本的回显服务 4.3 英译汉服务 5. ...

  4. 非阻塞socket的连接

    引用自:http://blog.csdn.net/cccallen/article/details/6619994 连接套接字,阻塞的套接字超时时间很长无法接受,而是用非阻塞套接字时使用的方案也有多种 ...

  5. 向一个无法连接的网络尝试了一个套接字操作_python3从零学习-5.8.1、socket—底层网络接口...

    源代码: Lib/socket.py 这个模块提供了访问BSD*套接字*的接口.在所有现代Unix系统.Windows.macOS和其他一些平台上可用. 这个Python接口是用Python的面向对象 ...

  6. UDP socket 设置为的非阻塞模式

    UDP socket 设置为的非阻塞模式  Len = recvfrom(SocketFD, szRecvBuf, sizeof(szRecvBuf), MSG_DONTWAIT, (struct s ...

  7. Socket阻塞与非阻塞,同步与异步、I/O模型

    [原文链接] 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步:       所谓同步,就是在发出一 ...

  8. [转]Socket的阻塞模式和非阻塞模式

    http://blog.csdn.net/VCSockets/ 阻塞模式 Windows套接字在阻塞和非阻塞两种模式下执行I/O操作.在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即 ...

  9. 基于MFC的socket编程(异步非阻塞通信)

    对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手.许多概念,诸如:同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)等,初学者往往迷惑不清,只知其 ...

最新文章

  1. 深度解析 PouchContainer 的富容器技术
  2. Laravel之路——file缓存修改为redis缓存
  3. 算法:柱状图中最大矩形
  4. 【转】匈牙利命名法(Hungarian Notation)
  5. service层的方法上使用@Transactional注解,程序发生异常没有回滚
  6. PL/SQL异常处理
  7. Oracle-1:的列操作(增加列,修改列,删除列),包括操作多列
  8. html前沿技术网页,HTML5新技术给网站带来了哪些改变
  9. bitbake如何clean
  10. 如何添加PR视频特效?
  11. 终端上网_家里wifi上网很慢怎么办?通过这4招,让网速快到飞起来
  12. KANBAN专题一:KANBAN管理的基本系统操作
  13. win10 蓝牙忽然消失,设备管理器有未知USB设备描述符请求失败
  14. 微信小程序个人和企业有什么不一样(个人和企业小程序的区别)
  15. 五子棋2.0(Java)
  16. fuz 1205(小鼠迷宫问题)
  17. 安装AUTOROM、导入ROMs
  18. 认证资料大全(八)------ SUN认证列表
  19. 基于Java基础的图书管理系统
  20. win10定时关机c语言,win10系统通过命令实现定时关机的操作方法

热门文章

  1. Linux设备模型分析之kset(基于3.10.1内核)
  2. 鼠标触碰div 字体向上移动
  3. 数值计算——拉格朗日插值方法(附代码)
  4. window.parent方法获取父页面属性或者调用方法
  5. 浏览器——google chrome
  6. Rejecting mapping update to [blog] as the final mapping would have more than 1 type: [doc, blog]
  7. 计算机设备管理项目表,计算机网络设备管理
  8. 菜鸟学python之乒乓球比赛排单
  9. EFCore-6 反向工程
  10. 大二初学者用c++实现平抛运动