epoll IO多路复用实现单线程支持上万并发代码实例

一、前言

 

  之前我们就讲了select的这种方式,使用的是轮询方式去监测客户端的连接,效率比较低下,我们今天来聊聊epoll的方式,这种效率更高,但是这种方式在Windows下不支持,在Linux是支持的,那就不得不说下面的一个模块selectors。

 二、selectors模块

 

2.1、英文解释

  This module allows high-level and efficient I/O multiplexing, built upon the select module primitives. Users are encouraged to use this module instead, unless they want precise control over the OS-level primitives used.

2.2、中文解释

  selectors 默认是epoll,如果找不到epoll的话,比如说windows操作系统,就会找select

 三、selectors模块使用

 

3.1、selectors实现非阻塞连接

说明:默认它是阻塞的,只要不阻塞了,就代表肯定有活动的数据,我就循环这个events。

import selectors,socket
 
sel = selectors.DefaultSelector()
 
def accept(sock,mask):
    "接收客户端信息实例"
    conn,addr = sock.accept()
    print("accepted",conn,'from',addr)
    conn.setblocking(False)
    sel.register(conn,selectors.EVENT_READ,read)  #新连接注册read回调函数
 
def read(conn,mask):
    "接收客户端的数据"
    data = conn.recv(1024)
    if data:
        print("echoing",repr(data),'to',conn)
        conn.send(data)
    else:
        print("closing",conn)
        sel.unregister(conn)
        conn.close()
 
server = socket.socket()
server.bind(('localhost',9999))
server.listen(500)
server.setblocking(False)
sel.register(server,selectors.EVENT_READ,accept)  #注册事件,只要来一个连接就调accept这个函数,
#sel.register(server,selectors.EVENT_READ,accept) == inputs=[server,]
 
while True:
    events = sel.select()  #这个select,看起来是select,有可能调用的是epoll,看你操作系统是Windows的还是Linux的
                           #默认阻塞,有活动连接就返回活动连接列表
    print("事件:",events)
    for key,mask in events:
        callback = key.data #相当于调accept了
        callback(key.fileobj,mask)  #key.fileobj=文件句柄

3.2、selectors用法

①定义一个对象

sel = selectors.DefaultSelector()

②注册一个事件

说明:注册事件,只要来一个连接就调accept这个函数,就相当于之前select的用法,sel.register(server,selectors.EVENT_READ,accept) ==  inputs=[server,],readable,writeable,exceptional = select.select(inputs,outputs,inputs)意思是一样的。

sel.register(server,selectors.EVENT_READ,accept) 

③循环事件

说明,这边这个events输出的是什么呐?

while True:
    events = sel.select()  #这个select,看起来是select,有可能调用的是epoll,看你操作系统是Windows的还是Linux的
    print("事件:",events)
    for key,mask in events:
        callback = key.data #相当于调accept了
        callback(key.fileobj,mask)  #key.fileobj=文件句柄

  打印events的结果:

事件: [(SelectorKey(fileobj=<socket.socket fd=276, family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>, fd=276, events=1,
data=<function accept at 0x000002722BB1D510>), 1)]
accepted <socket.socket fd=352, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM,
proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 63829)> from ('127.0.0.1', 63829)
 
事件: [(SelectorKey(fileobj=<socket.socket fd=352, family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1',
63829)>, fd=352, events=1, data=<function read at 0x000002722BB1D840>), 1)]
echoing b'ls' to <socket.socket fd=352, family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1',
63829)>

 这样很容易明白了:

callback = key.data #第一次调用的是accept,第二次调用的是read
callback(key.fileobj,mask)  #key.fileobj=文件句柄
 四、高并发的客户端代码

 

4.1、客户端高并发代码

说明:这个在Windows上试的时候是select,最好在Linux上试,它默认支持的是epoll

import socket,sys
 
messages = [ b'This is the message. ',
             b'It will be sent ',
             b'in parts.',
             ]
server_address = ('localhost', 9999)
 
# Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM) for i in range(100)]
 
# Connect the socket to the port where the server is listening
print('connecting to %s port %s' % server_address)
for s in socks:
    s.connect(server_address)
 
for message in messages:
 
    # Send messages on both sockets
    for s in socks:
        print('%s: sending "%s"' % (s.getsockname(), message) )
        s.send(message)
 
    # Read responses on both sockets
    for s in socks:
        data = s.recv(1024)
        print( '%s: received "%s"' % (s.getsockname(), data) )
        if not data:
            print(sys.stderr, 'closing socket', s.getsockname() )

  想了解更多关于epoll的东西:请猛击这里

 

转载于:https://www.cnblogs.com/xiangjun555/articles/7744230.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值