Python : Check if IRC connection is lost (PING PONG?)
Asked Answered
A

4

2

So my question is, how would i get my bot to listen if there is a PING and if there's no ping in an interval of a minute, it will react as though the connection has been lost. How would one go about doing that?

EDIT:

This is the working code for registering the connection fallout (although having trouble with reconnecting):

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import socket
import string
import os
import platform
import time

# Variables
HOST = "irc.channel.net"
PORT = 6667
NICK = "Botname"
IDENT = "Botname"
REALNAME = os.getenv('USER')
CHAN = "##ChannelName"
readbuffer = ""

# Our IRC connection
irc = socket.socket()
irc.settimeout(300)
connected = False
def connection(host, port, nick, ident, realname, chan):
    while connected is False:
        try:
            irc.connect((host, port))
            irc.send("NICK %s\r\n" % nick)
            irc.send("USER %s %s bla :%s\r\n" % (ident, host, realname))
            irc.send("JOIN :%s\r\n" % chan)
            # Initial msg to send when bot connects
            irc.send("PRIVMSG %s :%s\r\n" % (chan, "TehBot: "+ nick + " Realname: " + realname + " ."))
            global connected
            connected = True
        except socket.error:
            print "Attempting to connect..."
            time.sleep(5)
            continue
connection(HOST, PORT, NICK, IDENT, REALNAME, CHAN)

while connected:
    try:
        data = irc.recv ( 4096 )
        # If connection is lost
        if len(data) == 0:
            break
        print data
        # If Nick is in use
        if data.find ( "Nickname is already in use" ) != -1:
            NICK = NICK + str(time.time())
            connection(HOST, PORT, NICK, IDENT, REALNAME, CHAN)
        # Ping Pong so we don't get disconnected
        if data[0:4] == "PING":
            irc.send ( "PONG " + data.split() [ 1 ] + "\r\n" )
    except socket.timeout:
        global connected
        connected = False
        print connected
        break
print "Out of loop"
connection(HOST, PORT, NICK, IDENT, REALNAME, CHAN)
Akerboom answered 28/7, 2011 at 1:5 Comment(1)
Twisted is a lovely framework.Stabile
G
6
last_ping = time.time()
threshold = 5 * 60 # five minutes, make this whatever you want
while connected:
    data = irc.recv ( 4096 )
    # If Nick is in use
    if data.find ( 'Nickname is already in use' ) != -1:
        NICK = NICK + str(time.time())
        Connection()
    # Ping Pong so we don't get disconnected
    if data.find ( 'PING' ) != -1:
        irc.send ( 'PONG ' + data.split() [ 1 ] + '\r\n' )
        last_ping = time.time()
    if (time.time() - last_ping) > threshold:
        break

This will record the time each time it gets a ping, and if it goes too long without one, break out of the connected loop. You don't need while connected == True:, just while connected: does the same thing.

Also, consider using connection instead of Connection, it's the Python convention to use capitalized names only for classes.

Gorton answered 28/7, 2011 at 1:48 Comment(5)
Hey, I've been trying to implement your method, but seems like it doesn't keep comparing the last_ping and time.time(). When connection is lost (no more PINGs) it doesn't do anything. Dunno if I'm doing anything wrong. Have you tested it ?...Akerboom
I don't know what you mean by doesn't seem to compare the two. Either update your question or pastebin your code and post a link so I can see it? The above code will definitely break out of the loop after five minutes if data.find ( 'PING' ) != -1: doesn't happen. Try changing the threshold, try throwing in some print statements, tell me exactly what is happening.Gorton
Okay, here is pastebin link for the code I've been using and the output I get: pastebin.com/SCumqMiw . After the second PING has been sent, I terminate my connection and wait for way more than 5 minutes, just to be sure, but nothing happens (should print "Connection lost"). Dunno if I've missed some stupid mistake or my python is just acting irregularly :S thanks for the help so far btw :).. (and have tried to alter threshold, but same result)Akerboom
1. Make sure you have a print statement below the loop so you know if it broke out. 2. Does irc.recv block? If so, as soon as the connection is broken, you're stuck there forever waiting for 4096 bytes you're never going to get. You need to set a timeout if it's blocking. If that's not the problem, I don't know what is. Note that you don't both have to set connected = False and break, one or the other will do.Gorton
Okay, the problem was that it was blocking so once it failed, it didn't go out of the loop. I fixed it by setting socket.settimeout(300) :P... thx for the help man :)Akerboom
S
3

There's no reason to do any fancy 'timeout' tricks as long as your connection is still up. If the length of the data returned from recv is 0, the TCP connection has been closed.

data = irc.recv(4096)
if len(data) == 0:
    # connection closed
    pass

I suspect that recv() can also throw an exception if the connection is not terminated cleanly.

Edit:

I'm not sure what you're trying to accomplish. The IRC server will send you a PING occasionally. If you don't respond with a PONG, then the server will disconnect you. When the server disconnects you, then your recv() call will return a 0-length string.

All you have to do is respond to PING when you get it, and handle if the connection happens to close.

Your logic should look something like this:

keep_trying_to_connect = True
while keep_trying_to_connect:
    # try to connect
    irc = socket.socket()
    # send NICK, USER, JOIN here
    # now we're connected and authenticated start your recv() loop
    while True:
        data = irc.recv(4096)
        if len(data) == 0:
            # disconnected, break out of recv loop and try to reconnect
            break
        # otherwise, check what the data was, handling PING, PRIVMSG, etc.

Another thing to keep in mind is that you need to buffer any received data until you get a \r\n sequence, you will not always get a exactly one complete message at the same time; you might get half of one, or three, or three and a half lines.

Sheehy answered 28/7, 2011 at 1:56 Comment(2)
Doesn't that assume that there is a constant data flow? There is only the occasional ping, so don't know how it would know when exactly it should check for this (Tried it and didn't work)?Akerboom
okay, thx for the clarification. Found out that it was because socket.setblocking was set to block, but fixed this by setting a timeout limit. Okay, didn't know that much about IRC protocols and such, but I can see why your example would work too, thx :)Akerboom
F
2

You should not use data.find('PING') because it also finds "PING" in other messages. And then you send an incorrect PONG...

Instead try something like that:

if data[0:4] == "PING":
    irc.send("PONG " + data.split()[1] + "\n")
Fioritura answered 28/7, 2011 at 8:14 Comment(1)
more pythonic: if data.startswith('PING'): ...Fioritura
D
0

The reason why your having issues reconnecting is because once you do there's no loop to listen to for incoming data once you do, and you'd most likely ping timeout. The connection while loop should look like:

while connected:
    try:
        ...
    except socket.timeout:
        global connected
        connected = False
        print connected
        connection(HOST, PORT, NICK, IDENT, REALNAME, CHAN)
        continue
print "Out of loop"

Now when the connection times out you reconnect and begin to listen for incoming data.

NOTE: Now that there's no way for the application to terminate on it's on you have to either [Ctrl]+[C] in the command line, or construct a "!quit" type command to close the socket and the application... socket first, of course.

Dwight answered 22/9, 2011 at 16:4 Comment(1)
Hey, thx for the reply to the problem about reconnecting. Probably should have update my answer to leave no loose ends, but already got it working using a try, except statements to catch the socket timeout like you suggested.Akerboom

© 2022 - 2024 — McMap. All rights reserved.