Update 03.09.2019: The vpatches reground to sept_fixes are at:
curl 'http://bvt-trace.net/vpatches/active_disconnect_r3.kv.vpatch' > active_disconnect_r3.kv.vpatch curl 'http://bvt-trace.net/vpatches/active_disconnect_r3.kv.vpatch.bvt.sig' > active_disconnect_r3.kv.vpatch.bvt.sig
This patch modifies the behavior of logotron IRC bot to close the socket on error and initialize it anew when connecting to a new server. It also has some additional changes:
- I disabled Nagle's algorithm on a socket, as it has no business being enabled on low-bandwidth low-rate IRC links.
- I replaced 'Listen socket' with 'Receive socket' in the error messages: 'listen' is a term of art for sockets accepting connections, so it looked confusing to me - there is no listening in BSD sockets sense of this word anywhere in the bot.
- I also removed SO_KEEPALIVE socket option which got into hastily prepared initial version of vpatch. TCP keepalive messages are first sent after two hours of idleness on the socket - long after ~250 seconds of IRC idleness timeout expire.
def init_socket(): global sock sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Disable Nagle's algorithm for transmit operations sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # Disable Nagle's algorithm for receive operation, Linux-only try: sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1) except Exception as e: logging.warning(e) connected = False def deinit_socket(): global connected global sock sock.close() connected = False @@ -171,7 +188,7 @@ sock.send(message.encode("utf-8")) except (socket.timeout, socket.error) as e: logging.warning("Socket could not send! Disconnecting.") - connected = False + deinit_socket() return False except Exception as e: logging.exception(e) @@ -219,6 +236,10 @@ def irc(): global connected global time_last_conn + global sock + + # Initialize a socket + init_socket() # Connect to one among the specified servers, in given priority : while not connected: @@ -246,20 +267,20 @@ try: data = sock.recv(Buf_Size) except socket.timeout as e: - logging.debug("Listen timed out") + logging.debug("Receive timed out") continue except socket.error as e: - logging.warning("Listen socket error, disconnecting.") - connected = False + logging.warning("Receive socket error, disconnecting.") + deinit_socket() continue except Exception as e: logging.exception(e) - connected = False + deinit_socket() continue else: if len(data) == 0: - logging.warning("Listen socket closed, disconnecting.") - connected = False + logging.warning("Receive socket closed, disconnecting.") + deinit_socket() continue try: try:
It was tested only a little using strace's error injection:
$ strace -f -einject=sendto,recvfrom,connect:error=ECONNRESET:when=10+7 -p PID_OF_bot.py
Patches available here:
curl 'http://bvt-trace.net/vpatches/active_disconnect.kv.vpatch' > active_disconnect.kv.vpatch curl 'http://bvt-trace.net/vpatches/active_disconnect.kv.vpatch.bvt.sig' > active_disconnect.kv.vpatch.bvt.sig