class dispatcher(asyncore.dispatcher): connectChannel = None acceptChannel = None recvChannel = None def __init__(self, sock): # This is worth doing. I was passing in an invalid socket which was # an instance of dispatcher and it was causing tasklet death. if not isinstance(sock, stdsocket.socket): raise StandardError("Invalid socket passed to dispatcher") asyncore.dispatcher.__init__(self, sock) # if self.socket.type == SOCK_DGRAM: # self.dgramRecvChannels = {} # self.dgramReadBuffers = {} #else: self.recvChannel = Channel(micro_send=True) self.readBufferString = '' self.readBufferList = [] self.sendBuffer = '' self.sendToBuffers = [] self.maxreceivebuf=65536 def writable(self): if self.socket.type != SOCK_DGRAM and not self.connected: return True return len(self.sendBuffer) or len(self.sendToBuffers) def accept(self): if not self.acceptChannel: self.acceptChannel = Channel(micro_send=True) return self.acceptChannel.recv() def connect(self, address): asyncore.dispatcher.connect(self, address) # UDP sockets do not connect. if self.socket.type != SOCK_DGRAM and not self.connected: if not self.connectChannel: # Prefer the sender. Do not block when sending, given that # there is a tasklet known to be waiting, this will happen. self.connectChannel = Channel(prefer_sender=True) self.connectChannel.recv() def send(self, data): self.sendBuffer += data micro_block() return len(data) def sendall(self, data): # WARNING: this will busy wait until all data is sent # It should be possible to do away with the busy wait with # the use of a channel. self.sendBuffer += data while self.sendBuffer: micro_block() return len(data) def sendto(self, sendData, sendAddress): waitChannel = None for idx, (data, address, channel, sentBytes) in enumerate(self.sendToBuffers): if address == sendAddress: self.sendToBuffers[idx] = (data + sendData, address, channel, sentBytes) waitChannel = channel break if waitChannel is None: waitChannel = Channel(micro_send=True) self.sendToBuffers.append((sendData, sendAddress, waitChannel, 0)) return waitChannel.recv() # Read at most byteCount bytes. def recv(self, byteCount): self.maxreceivebuf=byteCount if len(self.readBufferString) < byteCount: # If our buffer is empty, we must block for more data we also # aggressively request more if it's available. if len(self.readBufferString) == 0 or self.recvChannel.ch.balance > 0: self.readBufferString += self.recvChannel.recv() # Disabling this because I believe it is the onus of the application # to be aware of the need to run the scheduler to give other tasklets # leeway to run. # stackless.schedule() ret = self.readBufferString[:byteCount] self.readBufferString = self.readBufferString[byteCount:] return ret def recvfrom(self, byteCount): ret = "" address = None self.maxreceivebuf=byteCount return self.recvChannel.recv() def close(self): asyncore.dispatcher.close(self) self.connected = False self.accepting = False self.sendBuffer = '' # breaks the loop in sendall # Clear out all the channels with relevant errors. if self.acceptChannel is not None: while self.acceptChannel and self.acceptChannel.ch.balance < 0: self.acceptChannel.send_exception(error, 9, 'Bad file descriptor') if self.connectChannel is not None: while self.connectChannel and self.connectChannel.ch.balance < 0: self.connectChannel.send_exception(error, 10061, 'Connection refused') if self.recvChannel is not None: while self.recvChannel and self.recvChannel.ch.balance < 0: # The closing of a socket is indicted by receiving nothing. The # exception would have been sent if the server was killed, rather # than closed down gracefully. self.recvChannel.ch.send("") #self.recvChannel.send_exception(error, 10054, 'Connection reset by peer') # asyncore doesn't support this. Why not? def fileno(self): # XXX: self.socket.fileno() raises a Bad file descriptor error. # Therefore, we're using _fileno as a hack. This has to be # cleaned. # return self.socket.fileno() return self._fileno def handle_accept(self): if self.acceptChannel and self.acceptChannel.ch.balance < 0: currentSocket, clientAddress = asyncore.dispatcher.accept(self) currentSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # Give them the asyncore based socket, not the standard one. currentSocket = self.wrap_accept_socket(currentSocket) self.acceptChannel.send((currentSocket, clientAddress)) # Inform the blocked connect call that the connection has been made. def handle_connect(self): if self.socket.type != SOCK_DGRAM and self.connectChannel is not None: self.connectChannel.send(None) # Asyncore says its done but self.readBuffer may be non-empty # so can't close yet. Do nothing and let 'recv' trigger the close. def handle_close(self): pass # Some error, just close the channel and let that raise errors to # blocked calls. def handle_expt(self): self.close() def handle_read(self): try: if self.socket.type == SOCK_DGRAM: ret, address = self.socket.recvfrom(self.maxreceivebuf) self.recvChannel.send((ret, address)) else: ret = asyncore.dispatcher.recv(self, self.maxreceivebuf) # Not sure this is correct, but it seems to give the # right behaviour. Namely removing the socket from # asyncore. if not ret: self.close() self.recvChannel.send(ret) except stdsocket.error, err: # XXX Is this correct? # If there's a read error assume the connection is # broken and drop any pending output if self.sendBuffer: self.sendBuffer = "" # Why can't I pass the 'err' by itself? self.recvChannel.ch.send_exception(stdsocket.error, err)
class dispatcher(asyncore.dispatcher): connectChannel = None acceptChannel = None recvChannel = None def __init__(self, sock): # This is worth doing. I was passing in an invalid socket which was # an instance of dispatcher and it was causing tasklet death. if not isinstance(sock, stdsocket.socket): raise StandardError("Invalid socket passed to dispatcher") asyncore.dispatcher.__init__(self, sock) # if self.socket.type == SOCK_DGRAM: # self.dgramRecvChannels = {} # self.dgramReadBuffers = {} #else: self.recvChannel = Channel(micro_send=True) self.readBufferString = '' self.readBufferList = [] self.sendBuffer = '' self.sendToBuffers = [] self.maxreceivebuf = 65536 def writable(self): if self.socket.type != SOCK_DGRAM and not self.connected: return True return len(self.sendBuffer) or len(self.sendToBuffers) def accept(self): if not self.acceptChannel: self.acceptChannel = Channel(micro_send=True) return self.acceptChannel.recv() def connect(self, address): asyncore.dispatcher.connect(self, address) # UDP sockets do not connect. if self.socket.type != SOCK_DGRAM and not self.connected: if not self.connectChannel: # Prefer the sender. Do not block when sending, given that # there is a tasklet known to be waiting, this will happen. self.connectChannel = Channel(prefer_sender=True) self.connectChannel.recv() def send(self, data): self.sendBuffer += data micro_block() return len(data) def sendall(self, data): # WARNING: this will busy wait until all data is sent # It should be possible to do away with the busy wait with # the use of a channel. self.sendBuffer += data while self.sendBuffer: micro_block() return len(data) def sendto(self, sendData, sendAddress): waitChannel = None for idx, (data, address, channel, sentBytes) in enumerate(self.sendToBuffers): if address == sendAddress: self.sendToBuffers[idx] = (data + sendData, address, channel, sentBytes) waitChannel = channel break if waitChannel is None: waitChannel = Channel(micro_send=True) self.sendToBuffers.append((sendData, sendAddress, waitChannel, 0)) return waitChannel.recv() # Read at most byteCount bytes. def recv(self, byteCount): self.maxreceivebuf = byteCount if len(self.readBufferString) < byteCount: # If our buffer is empty, we must block for more data we also # aggressively request more if it's available. if len(self.readBufferString ) == 0 or self.recvChannel.ch.balance > 0: self.readBufferString += self.recvChannel.recv() # Disabling this because I believe it is the onus of the application # to be aware of the need to run the scheduler to give other tasklets # leeway to run. # stackless.schedule() ret = self.readBufferString[:byteCount] self.readBufferString = self.readBufferString[byteCount:] return ret def recvfrom(self, byteCount): ret = "" address = None self.maxreceivebuf = byteCount return self.recvChannel.recv() def close(self): asyncore.dispatcher.close(self) self.connected = False self.accepting = False self.sendBuffer = '' # breaks the loop in sendall # Clear out all the channels with relevant errors. if self.acceptChannel is not None: while self.acceptChannel and self.acceptChannel.ch.balance < 0: self.acceptChannel.send_exception(error, 9, 'Bad file descriptor') if self.connectChannel is not None: while self.connectChannel and self.connectChannel.ch.balance < 0: self.connectChannel.send_exception(error, 10061, 'Connection refused') if self.recvChannel is not None: while self.recvChannel and self.recvChannel.ch.balance < 0: # The closing of a socket is indicted by receiving nothing. The # exception would have been sent if the server was killed, rather # than closed down gracefully. self.recvChannel.ch.send("") #self.recvChannel.send_exception(error, 10054, 'Connection reset by peer') # asyncore doesn't support this. Why not? def fileno(self): # XXX: self.socket.fileno() raises a Bad file descriptor error. # Therefore, we're using _fileno as a hack. This has to be # cleaned. # return self.socket.fileno() return self._fileno def handle_accept(self): if self.acceptChannel and self.acceptChannel.ch.balance < 0: currentSocket, clientAddress = asyncore.dispatcher.accept(self) currentSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # Give them the asyncore based socket, not the standard one. currentSocket = self.wrap_accept_socket(currentSocket) self.acceptChannel.send((currentSocket, clientAddress)) # Inform the blocked connect call that the connection has been made. def handle_connect(self): if self.socket.type != SOCK_DGRAM and self.connectChannel is not None: self.connectChannel.send(None) # Asyncore says its done but self.readBuffer may be non-empty # so can't close yet. Do nothing and let 'recv' trigger the close. def handle_close(self): pass # Some error, just close the channel and let that raise errors to # blocked calls. def handle_expt(self): self.close() def handle_read(self): try: if self.socket.type == SOCK_DGRAM: ret, address = self.socket.recvfrom(self.maxreceivebuf) self.recvChannel.send((ret, address)) else: ret = asyncore.dispatcher.recv(self, self.maxreceivebuf) # Not sure this is correct, but it seems to give the # right behaviour. Namely removing the socket from # asyncore. if not ret: self.close() self.recvChannel.send(ret) except stdsocket.error, err: # XXX Is this correct? # If there's a read error assume the connection is # broken and drop any pending output if self.sendBuffer: self.sendBuffer = "" # Why can't I pass the 'err' by itself? self.recvChannel.ch.send_exception(stdsocket.error, err)