import asyncore import socket import errno import re import circularlog import logging class LineHandler(asyncore.dispatcher_with_send): def __init__(self, sock): self.buffer = b"" asyncore.dispatcher_with_send.__init__(self, sock) self.crlf = 0 def handle_read(self): data = self.recv(8192) if data: parts = data.split(b"\n") if len(parts) == 1: self.buffer += data else: self.handle_line((self.buffer + parts[0]).decode('ascii')) for part in parts[1:-1]: if part[-1] == b"\r": self.crlf = True part = part[:-1] else: self.crlf = False self.handle_line(part.decode('ascii')) self.buffer = parts[-1] def send_line(self, line): self.send(line.encode('ascii') + (b"\r\n" if self.crlf else b"\n")) def handle_line(self, line): ''' test: simple echo handler ''' self.send_line("> " + line) class LineServer(asyncore.dispatcher): def __init__(self, host, port, lineHandlerClass): asyncore.dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((host, port)) self.listen(5) self.lineHandlerClass = lineHandlerClass def handle_accept(self): pair = self.accept() if pair is not None: sock, addr = pair print("Incoming connection from %s" % repr(addr)) handler = self.lineHandlerClass(sock) def loop(self): asyncore.loop() class Disconnected(Exception): pass class LineClient(object): def __init__(self, host_port, announcement=None, filter_ascii=False, ridername="r"): self.host_port = host_port self.filter_ascii = filter_ascii self.announcement = announcement self.circular = circularlog.Rider(ridername) self.connected = False def connect(self): logging.info("connect to %s %s", "%s:%d" % self.host_port, getattr(self, 'name', '?')) self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect(self.host_port) self.connected = True self.buffer = [b""] if self.announcement: self.send_line('\n'.join(self.announcement)) def get_line(self): if not self.connected: logging.info("connect for get_line") self.connect() while len(self.buffer) <= 1: self.socket.setblocking(0) try: data = self.socket.recv(1024) except socket.error as e: err = e.args[0] if err == errno.EAGAIN or err == errno.EWOULDBLOCK: return None raise e if data == "": print(self.buffer, '<') self.close() raise Disconnected("disconnected") self.socket.setblocking(1) data = data.split(b'\n') self.buffer[0] += data[0] for p in data[1:]: self.buffer.append(p) line = self.buffer.pop(0).decode('ascii') if len(line) > 0 and line[-1] == '\r': line = line[0:-1] self.circular.put("<", line) # print '<', line if self.filter_ascii: # replace non ascii characters line = re.sub(r'[^\x00-\x7E]+','?', line) return line def send_line(self, line): if not self.connected: logging.info("connect for cmd: %s", line) self.connect() # print '>', line self.circular.put(">", line) self.socket.sendall(line.encode('ascii') + b'\n') def close(self): self.socket.close() self.connected = False if __name__ == "__main__": server = LineServer("localhost", 9999, LineHandler) server.loop()