Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to know the if the socket connection is closed in python?

Tags:

python

sockets

I'm still confused about knowing the status of my socket connection. This is what I'm doing.

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",9979))

while True:
    msg = client.recv(4096)
    processNotificationMessage(msg)
    time.sleep(0.1)

I want to connect to a server and keep receiving data forever. But how can I know if my client connection was closed due to some reason?

By googling a lot all I found was that the client.recv() will return an empty string or client.recv() will throw an exception (but there was no exception when I stopped my server). But what if the server itself is sending an empty string sometimes? If I try to reconnect by checking for an empty string it throws an exception saying already connected. The explaination here (How to tell if a connection is dead in python) did not work for me.

No data was received by client when I restarted my server.

like image 257
Vishal Yarabandi Avatar asked Mar 08 '16 07:03

Vishal Yarabandi


2 Answers

fileno() will return -1 for closed sockets.

cli, addr = self.sock.accept()
print(cli.fileno())
cli.close()
print(cli.fileno())

OUTPUT

376
-1

From the docs 18.1 - Socket

socket.fileno() Return the socket’s file descriptor (a small integer), or -1 on failure. This is useful with select.select().

Also the socket is technically a list, with this you can find the [closed] tag

cli, addr = self.sock.accept()
for i in str(cli).split():
    print(i)
<socket.socket
fd=488,
family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM,
proto=0,
laddr=('10.1.1.2',
80),
raddr=('10.1.1.2',
16709)>
488

-1
cli.close()
for i in str(cli).split():
    print(i)
<socket.socket
fd=504,
family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM,
proto=0,
laddr=('10.1.1.2',
80),
raddr=('10.1.1.2',
16833)>
504
<socket.socket
[closed]   <---
fd=-1,
family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM,
proto=0>
-1
like image 63
omgimdrunk Avatar answered Oct 12 '22 13:10

omgimdrunk


First, it is worth saying that in most situations it seems best to handle this in the main handler loop where you can simply test for EOF (empty string received by socket.recv(). However, I came across the need to test for this and the following seemed better than passing another callback in to the thread to get this state, etc)

The following is what I am using, but I am very new to this though, so please provide feedback if there is a better or more efficient way to do this and I'll update this answer.

It seems like the best way to do this is to check getpeername() (testing socket.recv() here is bad since our test would alter the buffer, for example.)

Return the address of the remote endpoint. For IP sockets, the address info is a pair (hostaddr, port).

If this fails it raises a socket.error [Errno 9] Bad file descriptor.

def is_socket_valid(socket_instance):
    """ Return True if this socket is connected. """
    if not socket_instance:
        return False

    try:
        socket_instance.getsockname()
    except socket.error as err:
        err_type = err.args[0]
        if err_type == errno.EBADF:  # 9: Bad file descriptor
            return False

    try:
        socket_instance.getpeername()
    except socket.error as err:
        err_type = err.args[0]
    if err_type in [errno.EBADF, errno.ENOTCONN]:  #   9: Bad file descriptor.
        return False                               # 107: Transport endpoint is not connected

    return True

Note: I have the first if-check in there because I am testing an attribute that may be None as well.

  1. This isn't as explicit as I'd like but in my tests it seems to be accurate.
  2. The original question mentions wanting to know the reason for not being connected. This doesn't address that.

Additionally, another answer mentions fileno() returns a negative number if the local side of the socket is closed. I get the same error 9. Either way, this doesn't test the other side in the case that this is valid.

>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.fileno()
10
>>> s.close()
>>> s.fileno()
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
----> 1 s.fileno()

/tools/package/python/2.7.13/lib/python2.7/socket.pyc in meth(name, self, *args)
    226
    227 def meth(name,self,*args):
--> 228     return getattr(self._sock,name)(*args)
    229
    230 for _m in _socketmethods:

/tools/package/python/2.7.13/lib/python2.7/socket.pyc in _dummy(*args)
    172     __slots__ = []
    173     def _dummy(*args):
--> 174         raise error(EBADF, 'Bad file descriptor')
    175     # All _delegate_methods must also be initialized here.
    176     send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy

error: [Errno 9] Bad file descriptor
like image 27
Rafe Avatar answered Oct 12 '22 11:10

Rafe