Send datagram to all connections?

I’m putting together code that will send out a datagram to everybody connected to my server (used for stuff like server announcements, etc).

When somebody connects to my server, it adds their connection to a dictionary like this:

self.Connections[str(newConnection.this)] = rendezvous

That essentially creates an associative array of ID => connection for everybody connected to my server (I use the connection ID for a bunch of things).

So now I want to send out a datagram to everybody that is connected:

myAnnouncement = PyDatagram()
myAnnouncement.addString("HEEEYYYY YOU GUYYYYYYSSSS!")
for connectionID, connection in self.Connections():
	self.cWriter.send(myAnnouncement, connection)

Is there a more efficient way of doing this? I.e. is there any way to supply a list of connections to the send function directly? Or is this about as efficient as it will get? I did look through the documentation and see no way of putting a list/array as one of the arguments for the send function, but I want to make sure I didn’t overlook something.

My biggest concern is lag/server load. If I have 50 connections, that’s 50 packets I’m sending out for something really simple. It may not be much, but it adds up if you are doing this every second.

Bonus points if you can identify where that announcement comes from :wink:

That’s the right way to do it.

I don’t suppose you’re quoting from the old Electric Company intro?

David

Is that the most efficient method? Or is the some other way that might run faster?

My concern here is that supposed my datagram is 25 bytes. At 50 connections that consumes 1.25k bytes. Yeah that ain’t much (thankfully).

However at 5000 connections (yeah I know, the chances of my getting a game running with 5000 concurrent connections running is slim to none), that’s 102.5k bytes.

Now that I think of it, that’s actually not that bad if sending it out once/second. My home computer could probably handle that.

I was hoping there would be a way to address a packet to multiple connections, so instead of sending 50 identical packets, you could send 1 packet that would go to 50 clients.

Nope :stuck_out_tongue:

It doesn’t actually send the messages one at a time, even though it looks like that’s what you’re asking it to do. They get queued up and sent out to the different sockets in parallel by the operating system. So this is still pretty efficient.

Now, if you really wanted to do it super-efficiently, you’d use non-blocking I/O and code it all in C++. That’s the only way to scale up to super big. In Python, though, you ought to be able to handle at least a few hundred connections before you start to suffer.

David

5000 frames / sec would pretty much saturate a single 100MB link. A minimum UDP frame is around 100 bytes with no payload on the wire.

I have written network load testers in python and have achieved outbound transfer rates upto 8k frames / sec.

You can reach max byte transfer rates with max size frames (64k byte jumbograms).

So I finally tried to run this code and ran into a problem.

Here’s the code:

        for cid, connection in self.Connections.iteritems():
            self.cWriter.send(myDatagram, connection)

With that code Panda crashes, saying

So I changed the code to this:

        for cid, connection in self.Connections.iteritems():
            self.cWriter.send(myDatagram, connection.p())

And Panda crashes with a different error:

So what am I messing up?

Thanks again :slight_smile:

It means your connection is not a TCP socket connection. It must be something else, either a UDP connection, or a TCP rendezvous connection.

David

Hmmm…ok, then what do I need to fix?

Here’s the connection code:

    def StartConnectionManager(self):
        # this function creates a connection manager, and then 
        # creates a bunch of tasks to handle connections
        # that connect to the server
        self.cManager = QueuedConnectionManager()
        self.cListener = QueuedConnectionListener(self.cManager, 0)
        self.cReader = QueuedConnectionReader(self.cManager, 0)
        self.cWriter = ConnectionWriter(self.cManager,0)
        self.tcpSocket = self.cManager.openTCPServerRendezvous(self.port,self.backlog)
        self.cListener.addConnection(self.tcpSocket)
        self.portStatus = "Open"
        taskMgr.add(self.ConnectionManagerTASK_Listen_For_Connections,"Listening for Connections",-39)
        # This task listens for new connections
        taskMgr.add(self.ConnectionManagerTASK_Listen_For_Datagrams,"Listening for Datagrams",-40)
        # This task listens for new datagrams
        taskMgr.add(self.ConnectionManagerTASK_Check_For_Dropped_Connections,"Listening for Disconnections",-41)
        # This task listens for disconnections
        
    def ConnectionManagerTASK_Listen_For_Connections(self, task):
        if(self.portStatus == "Open"):
        # This exists in case you want to add a feature to disable your
        # login server for some reason.  You can just put code in somewhere
        # to set portStatus = 'closed' and your server will not
        # accept any new connections
            if self.cListener.newConnectionAvailable():
                print "CONNECTION"
                rendezvous = PointerToConnection()
                netAddress = NetAddress()
                newConnection = PointerToConnection()
                if self.cListener.getNewConnection(rendezvous,netAddress,newConnection):
                    newConnection = newConnection.p()
                    self.Connections[str(newConnection.this)] = rendezvous
                    # all connections are stored in the self.Connections
                    # dictionary, which you can use as a way to assign
                    # unique identifiers to each connection, making
                    # it easy to send messages out
                    self.cReader.addConnection(newConnection)   
                    
                    myResponse = PyDatagram()
                    sendCommand = self.CommandMatrix.MatrixVar("DECLARE_CONNECTION_ID")
                    myResponse.addUint8(sendCommand)
                    myResponse.addUint32(newConnection.this)
                    self.cWriter.send(myResponse, newConnection)
                    
                    print "\n---------------\nSOMEBODY CONNECTED"
                    print "IP Address: " + str(newConnection.getAddress())
                    print "Connection ID: " + str(newConnection.this)
                    print "---------------"
                    # you can delete this, I've left it in for debugging
                    # purposes
        return Task.cont  

What is this?

self.Connections[str(newConnection.this)] = rendezvous

Did you mean to write this instead, perhaps?

self.Connections[str(newConnection.this)] = newConnection

David

I was just thinking that when I was going to Starbucks lol. It worked like a charm.

Thanks once again Panda master :slight_smile: