>백엔드 개발 >파이썬 튜토리얼 >python select epoll poll 구문 분석에 대한 자세한 설명

python select epoll poll 구문 분석에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-03-15 13:20:012232검색

select, poll, epoll의 차이점

select

select는 1983년 4.2BSD에서 처음 등장했습니다. select() 시스템 호출 배열 기호(linux의 모든 것은 파일, 블록 장치, 소켓 연결 등입니다.) select()가 반환되면 배열의 준비된 파일 설명자가 플래그 비트를 커널에 의해 수정합니다. (준비 상태가 됨) 프로세스가 후속 읽기 및 쓰기 작업을 위해 이러한 파일 설명자를 얻을 수 있도록 (select는 네트워크 인터페이스 준비됨 상태 [네트워크 인터페이스에서 연결은 '파일'을 생성합니다.] 준비 상태가 된 후 선택하면 이 파일 설명자를 작동할 수 있습니다. [소켓서버는 멀티스레딩을 통해 여러 요청을 처리합니다. 각 연결에는 처리할 스레드가 할당되지만 선택은 단일 프로세스여야 하지만 이제는 하나의 프로세스를 전달해야 합니다. 동시성 효과를 달성하는 데 사용됩니다. 프로세스 아래에는 하나의 메인 스레드만 있습니다. 이는 동시성 효과를 달성하는 데 하나의 스레드가 사용된다는 의미입니다. 다중 동시성을 달성하기 위해 다중 스레드를 사용하는 대신 단일 프로세스를 사용하여 다중 동시성을 달성하는 이유는 무엇입니까?

==========답: 여러 스레드를 실행하는 것보다 하나의 프로세스에서 여러 동시성을 달성하는 것이 더 효율적이기 때문입니다. 여러 스레드를 시작하는 데 오버헤드가 많이 발생하고 CPU가 각 스레드의 상태를 지속적으로 확인하여 어떤 스레드가 실행될 수 있는지 확인해야 합니다. 이는 시스템에도 스트레스가 됩니다.

그렇다면 단일 프로세스가 다중 동시성을 달성하려면 어떻게 해야 할까요? ? ?

========답변: 생산자와 소비자 모델은 매우 교묘하게 사용됩니다. 생산자와 소비자는 비차단을 달성할 수 있습니다. 여기로 오세요. 이전 소켓 프로세스는 하나의 연결만 수신할 수 있으며 새 연결을 수신하면 차단됩니다. 왜냐하면 소켓 프로세스가 클라이언트와 먼저 통신해야 하고 두 프로세스가 서로를 기다리고 있기 때문입니다. [클라이언트가 메시지를 보내고 서비스가 클라이언트가 수신 중이고, 클라이언트가 반환을 기다리고 있습니다.... 서버가 수신을 기다리고 있습니다.] 이때 다른 연결이 있으면 이전 연결을 기다려야 합니다. -----------즉, 기본 소켓을 사용하여 다중 프로세스를 구현하는 것은 이 문제를 해결하기 위해 스레드를 생성하는 것입니다. 스레드가 너무 많으면 CPU에 대한 오버헤드와 부담이 상대적으로 커집니다.) 단일 소켓의 경우 차단되는 경우 대부분 IO 작업(네트워크 작업)을 기다리고 있습니다. IO 작업이기도 합니다). 이러한 상황을 피하기 위해 비동기식 ============= 클라이언트는 연결을 시작하고 서버에 파일 핸들을 등록합니다. 서버는 이러한 파일 핸들 목록을 지속적으로 폴링합니다. 스레드를 시작하지 않고 클라이언트와 연결을 설정합니다. 이때 메인 프로세스는 클라이언트와 상호 작용하고 다른 클라이언트는 메인 프로세스에 연결할 수 없습니다. 연결된 클라이언트뿐만 아니라 새 클라이언트와 연결을 설정할 때 클라이언트가 메시지를 보내는 동안 폴링은 매우 빨라지며(죽은

루프

) 클라이언트가 연결된 파일 핸들 목록을 새로 고칩니다. 서버는 이를 읽습니다. 메시지 후에는 클라이언트에 반환된 메시지를 수신할 다른 목록이 있으며, 이 목록은 새로 고쳐진 후 클라이언트에 반환됩니다. 완료되었지만 클라이언트와의 연결이 아직 중단되지 않았지만 다음 폴링에 들어갔습니다. ]

select의 장점

select는 현재 거의 모든 플랫폼에서 지원되며 우수한 크로스 플랫폼 기능을 갖추고 있습니다.

select 단점

select를 호출할 때마다 fd 컬렉션을 사용자 모드에서 커널 모드로 복사해야 합니다. 이 오버헤드는 많을 때 매우 커집니다. fds

단일 프로세스가 모니터링할 수 있는 fd 수에는 최대 제한이 있습니다. Linux에서는 기본값이 1024입니다(이 제한은 매크로 정의를 수정하거나 커널을 다시 컴파일하여 늘릴 수 있습니다)

그리고 선택한 fd가 배열에 배치되고 매번 전체 배열을 선형적으로 순회해야 하므로 fd가 많으면 오버헤드도 매우 큽니다.

python

select

select의 함수 호출은 읽기 가능하고 쓰기 가능하며 예외al = select.select(rlist, wlist, xlist[, timeout]), 처음 세 개의 매개변수는 각각 세 개의 목록이고 배열의 objects는 모두 대기 가능합니다. object: 모두 정수 설명자의 파일 설명입니다. (file 설명자) 또는 파일 설명자를 반환하는 fileno() 메서드가 있는 객체

rlist: 읽기 준비를 기다리는 목록

wlist: 쓰기 준비를 기다리는 목록

errlist: "예외" 목록을 기다리는 중

select 메소드는 파일 설명자를 모니터링하는 데 사용됩니다. 파일의 경우 설명자가 변경되면 설명자를 얻습니다.

1. 이 세 개의 목록은 빈 목록일 수 있지만 세 개의 빈 목록을 받는 것은 시스템에 따라 다릅니다(Linux에서는 허용되지만 Windows에서는 허용되지 않음).

2. rlist 시퀀스의 디스크립터가 읽기 가능한 경우(accetp 및 read) 변경된 디스크립터를 가져와서 읽기 가능한 시퀀스에 추가합니다.

3. 포함되면 시퀀스의 모든 설명자가 쓰기 가능한 시퀀스

에 추가됩니다. 4. errlist 시퀀스의 핸들에서 오류가 발생하면 오류 핸들이 예외 시퀀스

에 추가됩니다. 시간 초과가 설정되지 않은 경우 모니터링되는 설명자가 변경될 때까지 select가 차단됩니다.

시간 초과 = 1일 때 모니터링되는 핸들에 변경 사항이 없으면 Select는 1초 동안 차단한 다음 3을 반환합니다. 빈 목록. 모니터링되는 설명자(fd)가 변경되면 직접 실행됩니다.

6. Ptython 파일 개체(예: sys.stdin 또는 open() 및 os.open()에서 반환된 개체)는 목록에 허용될 수 있으며 소켓 개체는 소켓.socket()을 반환합니다. . 적절한 fileno() 메서드(실제로 임의의 정수가 아닌 파일 설명자를 반환해야 함)가 있는 한 클래스를 사용자 정의할 수도 있습니다.

select 예:

Python의 select() 메서드는 소켓, 열려 있는 파일 및 파이프(모두 fileno( ) 메소드의 파일 핸들)이 읽기 및 쓰기 가능해지거나 통신 오류가 발생하는 경우 select()를 사용하면 여러 연결을 동시에 모니터링하기 쉽고 여러 클라이언트 연결을 기다리고 모니터링하기 위해 긴 루프를 작성하는 것보다 효율적입니다. Python 인터프리터

#coding:UTF8

import select
import socket
import sys
import Queue

#创建一个TCP/IP 进程
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(0)

#连接地址和端口
server_address = ('localhost',10000)
print >>sys.stderr,'starting up on %s prot %s' % server_address
server.bind(server_address)

#最大允许链接数
server.listen(5)

inputs = [ server ]
outputs = []

message_queues = {}

while inputs:
    print >>sys.stderr,'\nwaiting for the next event'
    readable,writable,exceptional = select.select(inputs,outputs,inputs)

    # Handle inputs
    for s in readable:
     
        if s is server:
            # A "readable" server socket is ready to accept a connection
            connection, client_address = s.accept()
            print >>sys.stderr, 'new connection from', client_address
            #connection.setblocking(0)
            inputs.append(connection)
     
            # Give the connection a queue for data we want to send
            message_queues[connection] = Queue.Queue()
        
        else:
            data = s.recv(1024)
            if data:
                # A readable client socket has data
                print >>sys.stderr, 'received "%s" from %s' % (data, s.getpeername())
                message_queues[s].put(data)  #这个s相当于connection
                # Add output channel for response
                if s not in outputs:
                    outputs.append(s)

            else:
                # Interpret empty result as closed connection
                print >>sys.stderr, 'closing', client_address, 'after reading no data'
                # Stop listening for input on the connection
                if s in outputs:
                    outputs.remove(s)  #既然客户端都断开了,我就不用再给它返回数据了,所以这时候如果这个客户端的连接对象还在outputs列表中,就把它删掉
                inputs.remove(s)    #inputs中也删除掉
                s.close()           #把这个连接关闭掉
                 
                # Remove message queue
                del message_queues[s]
    
    # Handle outputs
    for s in writable:
        try:
            next_msg = message_queues[s].get_nowait()
        except Queue.Empty:
            # No messages waiting so stop checking for writability.
            print >>sys.stderr, 'output queue for', s.getpeername(), 'is empty'
            outputs.remove(s)
        else:
            print >>sys.stderr, 'sending "%s" to %s' % (next_msg, s.getpeername())
            s.send(next_msg.upper())
    # Handle "exceptional conditions"
    for s in exceptional:
        print >>sys.stderr, 'handling exceptional condition for', s.getpeername()
        # Stop listening for input on the connection
        inputs.remove(s)
        if s in outputs:
            outputs.remove(s)
        s.close()
     
        # Remove message queue
        del message_queues[s]     

server

를 통하지 않고 운영 체제에서 제공하는 C 네트워크 인터페이스를 통해 직접 작동하기 때문입니다.

#coding:UTF8

import socket
import sys
 
messages = [ 'This is the message. ',
             'It will be sent ',
             'in parts.',
             ]
server_address = ('localhost', 10003)
 
# Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
          socket.socket(socket.AF_INET, socket.SOCK_STREAM),
          ]
 
# Connect the socket to the port where the server is listening
print >>sys.stderr, '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 >>sys.stderr, '%s: sending "%s"' % (s.getsockname(), message)
        s.send(message)
 
    # Read responses on both sockets
    for s in socks:
        data = s.recv(1024)
        print >>sys.stderr, '%s: received "%s"' % (s.getsockname(), data)
        if not data:
            print >>sys.stderr, 'closing socket', s.getsockname()

client

코드 분석:

select() 메서드가 수신하고 모니터링합니다. 3 통신 목록, 첫 번째는 모든 입력 데이터로 외부에서 보낸 데이터를 의미하고, 두 번째는 전송될 모든 데이터(송신 데이터)를 모니터링하고 수신하는 것, 세 번째는 오류 정보

, 다음으로 select()에 전달할 입력 및 출력 정보를 포함하는 2개의 목록을 만들어야 합니다.


# 읽을 것으로 예상되는 소켓 입력 = [ 서버 ]# 읽을 소켓 writeoutputs = [ ]

클라이언트에서 들어오는 모든 연결과 데이터는 위 목록에 있는 서버의 메인 루프 프로그램에 의해 처리될 것으로 예상됩니다. 연결의 경우 쓰기 가능한 후에만 올 수 있으며 데이터를 수신하고 반환합니다(따라서 데이터 수신 후 즉시 반환되지 않음). 왜냐하면 각 연결은 먼저 입력 또는 출력 데이터를 캐시

해야 하기 때문입니다. 그런 다음 선택하여 꺼내어 보냅니다.


# Outgoing message queues (socket:Queue)message_queues = {}

서버 프로그램 루프의 main 부분, call

select()를 사용하여 네트워크 활동을 차단하고 기다립니다.

다음은 이 프로그램의 메인 루프입니다. select()가 호출되면 새로운 연결과 데이터가 들어올 때까지 차단하고 기다립니다.


입력하는 동안: # 적어도 하나의 소켓이 처리 준비가 될 때까지 기다립니다. print >>sys.stderr, 'nwaiting for the next event' 읽기 가능, 쓰기 가능, 예외 = select.select(inputs, Outputs , 입력)

입력, 출력, 예외(여기서는 입력과 공유됨)를 select()에 전달하면 3개의 새 목록이 반환됩니다. 모두 읽기 가능, 쓰기 가능, 예외로 할당되었습니다. 읽기 가능 목록의 소켓 연결은 수신(recv)할 수 있는 데이터를 나타냅니다. 쓰기 가능 목록의 모든 소켓 연결은 전송(전송)할 수 있는 소켓 연결을 저장합니다. 연결 통신에 오류가 발생하면 오류가 기록됩니다. 예외적으로.

Readable list 中的socket 可以有3种可能状态,第一种是如果这个socket是main "server" socket,它负责监听客户端的连接,如果这个main server socket出现在readable里,那代表这是server端已经ready来接收一个新的连接进来了,为了让这个main server能同时处理多个连接,在下面的代码里,我们把这个main server的socket设置为非阻塞模式。

 

第二种情况是这个socket是已经建立了的连接,它把数据发了过来,这个时候你就可以通过recv()来接收它发过来的数据,然后把接收到的数据放到queue里,这样你就可以把接收到的数据再传回给客户端了。

 

第三种情况就是这个客户端已经断开了,所以你再通过recv()接收到的数据就为空了,所以这个时候你就可以把这个跟客户端的连接关闭了。

 

对于writable list中的socket,也有几种状态,如果这个客户端连接在跟它对应的queue里有数据,就把这个数据取出来再发回给这个客户端,否则就把这个连接从output list中移除,这样下一次循环select()调用时检测到outputs list中没有这个连接,那就会认为这个连接还处于非活动状态

 

最后,如果在跟某个socket连接通信过程中出了错误,就把这个连接对象在inputs\outputs\message_queue中都删除,再把连接关闭掉

#coding:UTF8

import socket
import sys
 
messages = [ 'This is the message. ',
             'It will be sent ',
             'in parts.',
             ]
server_address = ('localhost', 10003)
 
# Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
          socket.socket(socket.AF_INET, socket.SOCK_STREAM),
          ]
 
# Connect the socket to the port where the server is listening
print >>sys.stderr, '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 >>sys.stderr, '%s: sending "%s"' % (s.getsockname(), message)
        s.send(message)
 
    # Read responses on both sockets
    for s in socks:
        data = s.recv(1024)
        print >>sys.stderr, '%s: received "%s"' % (s.getsockname(), data)
        if not data:
            print >>sys.stderr, 'closing socket', s.getsockname()

client

客户端程序展示了如何通过select()对socket进行管理并与多个连接同时进行交互,通过循环通过每个socket连接给server发送和接收数据。

server:
starting up on localhost prot 10000

waiting for the next event
new connection from ('127.0.0.1', 54812)

waiting for the next event
new connection from ('127.0.0.1', 54813)
received "This is the message. " from ('127.0.0.1', 54812)

waiting for the next event
received "This is the message. " from ('127.0.0.1', 54813)
sending "This is the message. " to ('127.0.0.1', 54812)

waiting for the next event
output queue for ('127.0.0.1', 54812) is empty
sending "This is the message. " to ('127.0.0.1', 54813)

waiting for the next event
output queue for ('127.0.0.1', 54813) is empty

waiting for the next event
received "It will be sent " from ('127.0.0.1', 54812)
received "It will be sent " from ('127.0.0.1', 54813)

waiting for the next event
sending "It will be sent " to ('127.0.0.1', 54812)
sending "It will be sent " to ('127.0.0.1', 54813)

waiting for the next event
output queue for ('127.0.0.1', 54812) is empty
output queue for ('127.0.0.1', 54813) is empty

waiting for the next event
received "in parts." from ('127.0.0.1', 54812)
received "in parts." from ('127.0.0.1', 54813)

waiting for the next event
sending "in parts." to ('127.0.0.1', 54812)
sending "in parts." to ('127.0.0.1', 54813)

waiting for the next event
output queue for ('127.0.0.1', 54812) is empty
output queue for ('127.0.0.1', 54813) is empty

waiting for the next event
closing ('127.0.0.1', 54813) after reading no data
closing ('127.0.0.1', 54813) after reading no data

waiting for the next event





client:
connecting to localhost port 10000
('127.0.0.1', 54812): sending "This is the message. "
('127.0.0.1', 54813): sending "This is the message. "
('127.0.0.1', 54812): received "THIS IS THE MESSAGE. "
('127.0.0.1', 54813): received "THIS IS THE MESSAGE. "
('127.0.0.1', 54812): sending "It will be sent "
('127.0.0.1', 54813): sending "It will be sent "
('127.0.0.1', 54812): received "IT WILL BE SENT "
('127.0.0.1', 54813): received "IT WILL BE SENT "
('127.0.0.1', 54812): sending "in parts."
('127.0.0.1', 54813): sending "in parts."
('127.0.0.1', 54812): received "IN PARTS."
('127.0.0.1', 54813): received "IN PARTS."

运行结果

poll 
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll() 的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

 

在Python中调用poll

select.poll(),返回一个poll的对象,支持注册和注销文件描述符。

poll.register(fd[, eventmask])注册一个文件描述符,注册后,可以通过poll()方法来检查是否有对应的I/O事件发生。fd可以是i 个整数,或者有返回整数的fileno()方法对象。如果File对象实现了fileno(),也可以当作参数使用。

eventmask是一个你想去检查的事件类型,它可以是常量POLLIN, POLLPRI和 POLLOUT的组合。如果缺省,默认会去检查所有的3种事件类型。

事件常量 意义

POLLIN 有数据读取

POLLPRT 有数据紧急读取

POLLOUT 准备输出:输出不会阻塞

POLLERR 某些错误情况出现

POLLHUP 挂起

POLLNVAL 无效请求:描述无法打开

poll.modify(fd, eventmask) 修改一个已经存在的fd,和poll.register(fd, eventmask)有相同的作用。如果去尝试修改一个未经注册的fd,会引起一个errno为ENOENT的IOError。

poll.unregister(fd)从poll对象中注销一个fd。尝试去注销一个未经注册的fd,会引起KeyError。

poll.poll([timeout])去检测已经注册了的文件描述符。会返回一个可能为空的list,list中包含着(fd, event)这样的二元组。 fd是文件描述符, event是文件描述符对应的事件。如果返回的是一个空的list,则说明超时了且没有文件描述符有事件发生。timeout的单位是milliseconds,如果设置了timeout,系统将会等待对应的时间。如果timeout缺省或者是None,这个方法将会阻塞直到对应的poll对象有一个事件发生。

#coding: utf-8 

import select, socket

response = b"hello world"

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('localhost', 10000))
serversocket.listen(1)
serversocket.setblocking(0)

#
poll = select.poll()
poll.register(serversocket.fileno(), select.POLLIN)

connections = {}
while True:
    for fd, event in poll.poll():
        if event == select.POLLIN:
            if fd == serversocket.fileno():
                con, addr = serversocket.accept()
                poll.register(con.fileno(), select.POLLIN)
                connections[con.fileno()] = con
            else:
                con = connections[fd]
                data = con.recv(1024)
                if data:
                    poll.modify(con.fileno(), select.POLLOUT)
        elif event == select.POLLOUT:
            con = connections[fd]
            con.send(response)
            poll.unregister(con.fileno())
            con.close()

epoll 
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表 就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了 这些文件描述符在系统调用时复制的开销。

另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描 述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调 机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

 

在Python中调用epoll

select.epoll([sizehint=-1])返回一个epoll对象。

eventmask

事件常量 意义

EPOLLIN 读就绪

EPOLLOUT 写就绪

EPOLLPRI 有数据紧急读取

EPOLLERR assoc. fd有错误情况发生

EPOLLHUP assoc. fd发生挂起

EPOLLRT 设置边缘触发(ET)(默认的是水平触发)

EPOLLONESHOT 设置为 one-short 行为,一个事件(event)被拉出后,对应的fd在内部被禁用

EPOLLRDNORM 和 EPOLLIN 相等

EPOLLRDBAND 优先读取的数据带(data band)

EPOLLWRNORM 和 EPOLLOUT 相等

EPOLLWRBAND 优先写的数据带(data band)

EPOLLMSG 忽视

epoll.close()关闭epoll对象的文件描述符。

epoll.fileno返回control fd的文件描述符number。

epoll.fromfd(fd)用给予的fd来创建一个epoll对象。

epoll.register(fd[, eventmask])在epoll对象中注册一个文件描述符。(如果文件描述符已经存在,将会引起一个IOError)

epoll.modify(fd, eventmask)修改一个已经注册的文件描述符。

epoll.unregister(fd)注销一个文件描述符。

epoll.poll(timeout=-1[, maxevnets=-1])等待事件,timeout(float)的单位是秒(second)。

#coding:Utf8
import socket, select

EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response  = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!'

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('localhost', 10000))
serversocket.listen(1)
serversocket.setblocking(0)

epoll = select.epoll()
epoll.register(serversocket.fileno(), select.EPOLLIN)

try:
   connections = {}; requests = {}; responses = {}
   while True:
      events = epoll.poll(1)
      for fileno, event in events:
         if fileno == serversocket.fileno():
            connection, address = serversocket.accept()
            connection.setblocking(0)
            epoll.register(connection.fileno(), select.EPOLLIN)
            connections[connection.fileno()] = connection
            requests[connection.fileno()] = b''
            responses[connection.fileno()] = response
         elif event & select.EPOLLIN:
            requests[fileno] += connections[fileno].recv(1024)
            if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
               epoll.modify(fileno, select.EPOLLOUT)
               print('-'*40 + '\n' + requests[fileno].decode()[:-2])
         elif event & select.EPOLLOUT:
            byteswritten = connections[fileno].send(responses[fileno])
            responses[fileno] = responses[fileno][byteswritten:]
            if len(responses[fileno]) == 0:
               epoll.modify(fileno, 0)
               connections[fileno].shutdown(socket.SHUT_RDWR)
         elif event & select.EPOLLHUP:
            epoll.unregister(fileno)
            connections[fileno].close()
            del connections[fileno]
finally:
   epoll.unregister(serversocket.fileno())
   epoll.close()
   serversocket.close()


위 내용은 python select epoll poll 구문 분석에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.