This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: telnetlib insensitive to connection loss
Type: behavior Stage: resolved
Components: IO Versions:
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: eric.smith, xy.zzy
Priority: normal Keywords:

Created on 2011-10-05 21:08 by xy.zzy, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (4)
msg144972 - (view) Author: xy zzy (xy.zzy) Date: 2011-10-05 21:08
Using python's telnetlib I can connect and communicate with a device.

While the telnet session is active I can disconnect the network cable of the device.

At this point, I would expect read_until() with a timeout to throw a socket.error, EOFError or perhaps an IOError, but what I actually get is a null string.

Because I'm reading in a loop, when the cable is reconnected the device will resume communicating, and the program will continue.

My best guess ts that read_until() or perhaps everything except open() is insensitive to the loss of a connection.
msg144973 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2011-10-05 21:50
Can you post some example code?

I would not expect disconnecting the network cable to close any TCP connections, unless you are transmitting data and/or you have keepalives turned on.
msg145005 - (view) Author: xy zzy (xy.zzy) Date: 2011-10-06 13:06
Cfrom class():

        # see if we can connect to pcPart
        try:
            self.pcPart = telnetlib.Telnet(IP, PORT)
            # clear the buffer 
            for i in range(10):
                self.pcPart.write('\n')
                r = self.pcPart.read_until('Prompt>', 1)

        except socket.error, e:
            logging.debug('socket.error: %d: %s' % (e.args[0], e.args[1]))
            self.pcPart.close()
            self.pcPart = None

from init():

    def talk(self,cmd,ret):

        """talk to the device"""

        read_chars = ""

        while (read_chars == ""):
            try:
                read_chars=""
                # get to the Prompt> prompt
                # logging.debug('seeking prompt')
                while (read_chars != 'Prompt>'):
                    self.pcPart.write("\n")
                    raw_data = self.pcPart.read_until('Prompt>', 1).split('\n')
                    # logging.debug('raw_data: %i %s' % (len(raw_data), raw_data))
                    read_chars = raw_data[2] 
                    # logging.debug('read_chars: %s' % (read_chars))
                    
                # send the command
                # logging.debug('found prompt')
                cmdx = (('xyzzy:%s\n') % cmd)
                self.pcPart.write(cmdx)
                # logging.debug('command %s, %s' % (cmd, cmdx))
                if (ret):
                    while ((len(raw_data) > 0) and ('{' not in read_chars)):
                        raw_data = self.pcPart.read_until('Prompt>', 1) 
                        # logging.debug('raw_data: %i %s' % (len(raw_data), raw_data))
                        try: 
                            read_chars = str(raw_data.split('\n\r')[1][1:-1])
                        except:
                            read_chars = ''
                            # logging.debug('read_chars: %s' % (read_chars))
                else:
                    raw_data = self.pcPart.read_until('Prompt>', 1)
                    # logging.debug('ret read: %s' % (raw_data))
                    read_chars = '@'
                    return read_chars

            except IndexError, e:
                logging.debug('IndexError: %d: %s' % (e.args[0], e.args[1]))
                traceback.print_exc(file=open(LOG_FILENAME, 'a'))
                read_chars = '@'
                time.sleep(1)

            except (IOError, socket.error), e:
                logging.debug('socket.error: %d: %s' % (e.args[0], e.args[1]))
                traceback.print_exc(file=open(LOG_FILENAME, 'a'))
                self.pcPart.close()
                self.pcPart = None
                logging.debug('reconnecting...')
                self.pcPart = telnetlib.Telnet(IP, PORT) 
                # clear the buffer 
                for i in range(10):
                    self.pcPart.write('\n')
                    r = self.pcPart.read_until('Prompt>', 1)
                read_chars = '@'
                logging.debug('reconnected')

        # clear the buffer 
        for i in range(2):
            self.pcPart.write('\n')
            r = self.pcPart.read_until('Prompt>', 1)
        # logging.debug('Data Read: ' + read_chars)
        return read_chars  

called from:

                DATA = self.talk('cmd', True)
                logging.debug('talk: %s' % (DATA))
msg145015 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2011-10-06 14:29
Assuming that you're unplugging the cable when the code is in the loop that occurs after the line "self.pcPart.write(cmdx)", and also assuming that you haven't turned on keepalives, then the behavior you see is expected.

You're just waiting to read some data, and there are no pending writes. Therefore the TCP stack will wait forever if there are no incoming packets. There could be no incoming packets due to no data being ready, of from the network being down. The TCP stack has no way of knowing, so it cannot notify your code.

I suggest turning on TCP keepalives, which would then allow the TCP stack to notify your code that the connection has been closed.

I'm going to close this issue. If you turn on keepalives and still see this problem, please reopen it.
History
Date User Action Args
2022-04-11 14:57:22adminsetgithub: 57318
2011-10-06 14:29:09eric.smithsetstatus: open -> closed
resolution: not a bug
messages: + msg145015

stage: resolved
2011-10-06 13:06:03xy.zzysetmessages: + msg145005
2011-10-05 21:50:05eric.smithsetnosy: + eric.smith
messages: + msg144973
2011-10-05 21:08:08xy.zzycreate