- #在linux的IO多路复用中有水平触发,
- 边缘触发两种模式,
- 这两种模式的区别如下: ##水平触发: 如果文件描述符已经就绪可以非阻塞的执行IO操作了,
- 此时会触发通知.允许在任意时刻重复检测IO的状态,
- #没有必要每次描述符就绪后尽可能多的执行IO.select,
- poll就属于水平触发.##边缘触发: 如果文件描述符自上次状态改变后有新的IO活动到来,
- 此时会触发通知.在收到一个IO事件通知后要尽可能#多的执行IO操作,
- 因为如果在一次通知中没有执行完IO那么就需要等到下一次新的IO活动到来才能获取到就绪的描述#符.信号驱动式IO就属于边缘触发.
select调用是内核级别的,select轮询相对非阻塞的轮询的区别在于—前者可以等待多个socket,能实现同时对多个IO端口进行监听,当其中任何一个socket的数据准好了,就能返回进行可读,然后进程再进行recvfrom系统调用,将数据由内核拷贝到用户进程,当然这个过程是阻塞的。
server端
- #__author: greg
- #date: 2017/9/24 21:52
- import socket
- sk1=socket.socket()
- sk1.bind((‘127.0.0.1‘,8001))
- sk1.listen()
- sk2=socket.socket()
- sk2.bind((‘127.0.0.1‘,8002))
- sk2.listen()
- sk3=socket.socket()
- sk3.bind((‘127.0.0.1‘,8003))
- sk3.listen()
- # inputs=[sk1,sk2,sk3,]
- inputs=[sk1,]
- import select
- while True:
- # [sk1, sk2,sk3,],select内部自动监听sk1,sk2,sk3三个对象,一旦某个句柄发生变化
- # 如果有人链接sk1
- # r_list=[sk1] #如果有人第一次连接,,sk1发生变化
- #句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
- r_list,w_list,e_list=select.select(inputs,[],[],1)
- print(r_list)
- for sk in r_list:
- #conn每一个连接对象
- conn,address=sk.accept()
- conn.sendall(bytes(‘hello‘,encoding=‘utf8‘))
- conn.close()
- # while True:
- # conn, address = sk.accept()
- # while True:
- # content_bytes=conn.recv(1024)
- # content_str=str(content_bytes,encoding=‘utf8‘)
- # conn.sendall(bytes(content_bytes+‘好‘,encoding=‘utf8‘))#sendall就是用while循环调用send
- # conn.close()
client端
- #__author: greg
- #date: 2017/9/24 22:30
- import socket
- obj=socket.socket()
- obj.connect((‘127.0.0.1‘,8001))
- content=str(obj.recv(1024),encoding=‘utf8‘)
- print(content)
- obj.close()
实例,并发聊天
- sk=socket.socket()
- sk.bind(("127.0.0.1",8801))
- sk.listen(5)
- inputs=[sk,]
- while True:
- r,w,e=select.select(inputs,[],[],5)
- print(len(r))
- for obj in r:
- if obj==sk:
- conn,add=obj.accept()
- print(conn)
- #[<socket.socket fd=420, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 8801), raddr=(‘127.0.0.1‘, 55504)>]
- inputs.append(conn)
- else:
- data_byte=obj.recv(1024)
- print(str(data_byte,‘utf8‘))
- inp=input(‘回答%s号客户>>>‘%inputs.index(obj))
- obj.sendall(bytes(inp,‘utf8‘))
- print(‘>>‘,r)
文件描述符其实就是咱们平时说的句柄,只不过文件描述符是linux中的概念。注意,我们的accept或recv调用时即向系统发出recvfrom请求
(1) 如果内核缓冲区没有数据--->等待--->数据到了内核缓冲区,转到用户进程缓冲区;
(2) 如果先用select监听到某个文件描述符对应的内核缓冲区有了数据,当我们再调用accept或recv时,直接将数据转到用户缓冲区。
如何在某一个client端退出后,不影响server端和其它客户端正常交流
来源: http://www.bubuko.com/infodetail-2407217.html