Detecting when a user disconnects

I just spent the last 3 hours making a very basic client/server connection, so I’m probably missing something obvious here, but my brain is really fried after going through the manual and troubleshooting all the code in the examples it gives.

Do I just add a task to check all connections to see if somebody has disconnected? Or is there some other way?

My client code detects if the connection has been severed, but I need the server to detect if the client has gone offline as well.

Here’s what I’ve got right now (the server takes connections, and the client just connects and sends a datagram containing ‘Hello Server!’):

Server Code:

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject   
from direct.task import Task
from pandac.PandaModules import QueuedConnectionManager, QueuedConnectionReader, ConnectionWriter, QueuedConnectionListener, NetAddress
from panda3d.core import *
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator


class Server(DirectObject):
    def __init__( self ):
        print "\n\nInitializing Server\n\n"
        self.cManager = QueuedConnectionManager()
        self.cListener = QueuedConnectionListener(self.cManager, 0)
        self.cReader = QueuedConnectionReader(self.cManager, 0)
        self.cWriter = ConnectionWriter(self.cManager,0)
 
        self.activeConnections=[] 
        
        self.port = 9099
        self.backlog = 1000
        print "Port Configuration:"
        print "       Port:             " + str(self.port)
        print "       Backlog Limiter:  " + str(self.backlog)
        self.tcpSocket = self.cManager.openTCPServerRendezvous(self.port, self.backlog)
        self.cListener.addConnection(self.tcpSocket)
        
        taskMgr.add(self.tskListenerPolling,"Poll the connection listener",-39)
        print "taskListenerPolling enabled - now listening on designated port"
        
        taskMgr.add(self.tskReaderPolling,"Poll the connection reader",-40)
        print "taskReaderPolling enabled - now scanning packets on port"

    def processDatagram(self, netDatagram):
        PRINT_MESSAGE = 1
        myIterator = PyDatagramIterator(netDatagram)
        msgID = myIterator.getUint8()
        if msgID == PRINT_MESSAGE:
            messageToPrint = myIterator.getString()
            print messageToPrint

    def tskListenerPolling(self, taskdata):
        
        if self.cListener.newConnectionAvailable():
 
            rendezvous = PointerToConnection()
            netAddress = NetAddress()
            newConnection = PointerToConnection()
 
            if self.cListener.getNewConnection(rendezvous,netAddress,newConnection):
                newConnection = newConnection.p()
                self.activeConnections.append(newConnection) # Remember connection
                self.cReader.addConnection(newConnection)     # Begin reading connection
        
        return Task.cont
    def tskReaderPolling(self, taskdata):
        if self.cReader.dataAvailable():
            datagram=NetDatagram() # packet received by server
            if self.cReader.getData(datagram):
                self.processDatagram(datagram) # what to do with it
        return Task.cont
    
    
Server = Server()
run()

Client Code:

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject   
from direct.task import Task
from pandac.PandaModules import QueuedConnectionManager, QueuedConnectionReader, ConnectionWriter, QueuedConnectionListener
from direct.distributed.PyDatagram import PyDatagram

class Client(DirectObject):
    def __init__( self ):
        print "Initializing client test"
        
        self.port = 9099
        self.ip_address = "localhost"
        self.timeout = 3000             # 3 seconds to timeout
        
        self.cManager = QueuedConnectionManager()
        self.cListener = QueuedConnectionListener(self.cManager, 0)
        self.cReader = QueuedConnectionReader(self.cManager, 0)
        self.cWriter = ConnectionWriter(self.cManager,0)
        
        self.Connection = self.cManager.openTCPClientConnection(self.ip_address, self.port, self.timeout)
        if self.Connection:
            print "Connected to Server"
            self.cReader.addConnection(self.Connection)
            PRINT_MESSAGE = 1
            myPyDatagram = PyDatagram()
            myPyDatagram.addUint8(PRINT_MESSAGE)
            myPyDatagram.addString("Hello Server!")
            self.cWriter.send(myPyDatagram, self.Connection)
            self.cManager.closeConnection(self.Connection)
            print "Disconnected from Server"
        else:
            print "Not connected to Server"
        
        
    
Client = Client()
run()

The QueuedConnectionManager has a method called resetConnectionAvailable(). Call it once a frame. If it returns true, call getResetConnection() to determine which connection was lost.

David

Cool, that pointed me in the right direction, but I can’t seem to close the connection once the user disconnects.

Here’s the error I’m getting:

The documentation says that I need to explicity call closeConnection once it’s been determined that one of the connections has died, so that’s what I’m trying to do. Here’s the relevant code:

    def taskCheckConnections(self, task):
        if(self.cManager.resetConnectionAvailable()):
            connectionLost = self.cManager.getResetConnection(PointerToConnection())
            self.cManager.closeConnection(self.activeConnections(connectionLost))
            print "Connection has been reset: " + str(connectionLost)
            print "Current connections: " + str(self.activeConnections)
        return Task.again

Basically I’m trying to call closeConnection by using the pointer from the activeConnections array, but this doesn’t seem to be working.

So how do I isolate the one specific connection so I can call closeConnection on it?

Edit: I did try passing PointerToConnection() to it as well, that didn’t work, it said it required an actual connection for the closeConnection function.

Have you tried to pass the specific element from self.activeConnections(connectionLost) to closeConnection() ? Appears from the error that you’re sending a list as the argument and the method being called is looking for something else.

If it’s something else, feel free to ignore.

Thanks for trying kimja, but that didn’t work.

However, Google pointed me to a thread here from over 3 years ago that had the answer (linky), seems drwr posted the solution in 2007.

My original code was right, but I wasn’t using .p() to get the actual connection. It works now :slight_smile:

    def taskCheckConnections(self, task):
        if(self.cManager.resetConnectionAvailable()):
            connectionPointer = PointerToConnection()
            self.cManager.getResetConnection(connectionPointer)
            lostConnection = connectionPointer.p()
            
            
            self.cManager.closeConnection(self.activeConnections(connectionLost))
            print "Connection has been reset: " + str(lostConnection)
            
            self.activeConnections.remove(lostConnection)
            #print "Current connections: " + str(self.activeConnections)
            for i in self.activeConnections:
                print i
        return Task.again