选择器套接字OSError:[Errno 9]子进程中的文件描述符错误

时间:2019-03-26 07:42:57

标签: python

我的开发环境是macos mojave。我用名为selectors.DefaultSelector()的类封装了Server。此类用于处理客户端并发请求。如果该类继承自multiprocessing.Process并尝试在子进程中运行错误,则会发生以下错误:

Self.sel1.register(self.listenSock, selectors.EVENT_READ, data=None)
   File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/selectors.py", line 522, in register
     Self._selector.control([kev], 0, 0)
OSError: [Errno 9] Bad file descriptor

但是它在主进程中运行(不继承multiprocessing.Process),那么如何解决此问题?

import types
import time
import multiprocessing as mp
Socket=socket.socket
request_que=mp.Queue()
def finditer(data,sub:str,count:int):
    #Continuous traversal to find the specified character
    pos=0
    while count>0:
        i=data[pos+1:].find(sub)
        if i>=0:
            pos+=(i+1)
        count-=1
    #end-while
    return pos
#end-def

def get_host_info(req:bytes):
    '''
    Get information about the requesting server from the Socket 
    request byte stream
    '''
    host,port,url,is_https='','','',False

    def extract_ip_port(req,method=b'GET'):
        ed=req.index(b' HTTP/1.1')
        url=req[len(method)+1:ed]
        st=finditer(url,b'/',2)+1
        ed=finditer(url,b'/',3)
        server=url[st:ed]
        pos=server.rfind(b':')
        if pos>0:
            port=int(server[pos+1:])
            host=server[:pos]
            if host.startswith(b'['): #ipv6
                host=host[1:-1]
        else:
            port=80
            host=server
        #end-if
        return host,port,url
    #end-def

    if req.startswith(b'GET'):
        host,port,url=extract_ip_port(req)

    elif req.startswith(b'POST'):
        host,port,url=extract_ip_port(req,method=b'POST')

    elif req.startswith(b'CONNECT'):
        ed=req.index(b' HTTP/1.1')
        req=req[8:ed]
        req=req.split(b':')
        host,port=req[0],req[1]
        is_https=True
    return host,port,url,is_https
#end-def
class Server(mp.Process):
    def __init__(self,locIP,locPort,reqQue):
        super(Server, self).__init__()
        self.locIP=locIP
        self.locPort=locPort  
        self.listenSock=None
        self.reqQue=reqQue
        self.sel1=selectors.DefaultSelector()
    def accept_wrapper(self,sock: socket.socket):
        '''
        Since the listened socket is registered to selectors.EVENT_READ, it can now be read, and immediately call sock.accept and conn.setblocking(False) 
        to get the socket into non-blocking mode.
        '''
        conn, addr = sock.accept()
        print('accepted connection from ', addr)
        conn.setblocking(False)
        conn.settimeout(6)
        #Dynamically create a temporary object class, data is used to track 
        #the data sent and received by each socket object

        data = types.SimpleNamespace(addr=addr, inb=b'', outb=b'')
        events = selectors.EVENT_READ | selectors.EVENT_WRITE
        self.sel1.register(conn, events, data=data)
    # end-def
    def run(self):
        try:
            self.listenSock =Socket(socket.AF_INET, socket.SOCK_STREAM)
            self.listenSock.setblocking(False)
            self.listenSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.listenSock.bind((self.locIP,self.locPort))
            self.listenSock.listen(5)
            print(f'Server Process:{os.getpid()}')
            print(f'Server is running & listen on {self.locIP}:{self.locPort}')
            #Selector.register() Registers socket monitoring for the event you 
            #are interested in using selector.select() . For listening sockets, 
            #we want to use selectors.EVENT_READ
            self.sel1.register(self.listenSock, selectors.EVENT_READ, data=None)
        except BlockingIOError as e:
            raise e
        except OSError as e:
            raise e
        try:
            while True:
                events = self.sel1.select(timeout=None)
                for key, mask in events:
                    # If None indicates a socket for listening
                    if key.data is None:
                        self.accept_wrapper(key.fileobj)
                    else:
                        self.service_connection(key, mask)
                # end-for
            # end-while
        except KeyboardInterrupt:
            print('keyboard interrupt by user,server exit')
        except OSError as e:
            print(e)
        finally:
            self.sel1.close()
    # end-def
    def service_connection(self,key, mask):
        sock = key.fileobj
        data = key.data
        if mask & selectors.EVENT_READ:
            request = sock.recv(MAX_RECV_BUF)
            if request:
                data.inb += request
                host,port,url,is_https=get_host_info(data.inb)

                request_que.put((host,port,url,is_https,data.inb))
            else:
                # This means that no data has been received and the client's 
                # socket object has been closed.
                print('closing connection to ', data.addr)
                # Undo monitoring of the current sock object               
                self.sel1.unregister(sock)
                sock.close()
        if mask & selectors.EVENT_WRITE:
            if data.outb:
                print(f'Main-Thread {sock}:{data.outb}')
                print('echoing', repr(data.outb), ' to ', data.addr)
                sent = sock.send(data.outb)
                data.outb = data.outb[sent:]
        # end-if
    # end-def
#end-class
if __name__=='__main__':         
     server=Server(args.host,args.port,request_que)
     server.start()

0 个答案:

没有答案