Threaded networking and blocked run()

My network code for my client is in a threaded (using panda’s threading) class. When the application launches the thread the main game engine seems to pause. messenger.toggleVerbose() only progresses when information is received or sent. Here is the link to my netcode. Any ideas or suggestions?

github.com/croxis/CityMania/blob … network.py

Ah, you are using Python sockets, which blocks the whole process, because it doesn’t know about Panda threads. Note that in Panda’s default compilation mode, “threads” are not true threads at all, but are simulated within the Panda code, which provides a lower overhead overall.

You should either put the sockets into non-blocking I/O (and use polling rather than blocking, with a call to Thread.considerYield() in the loop), or switch to using Panda sockets instead (which does this for you), or recompile Panda yourself to use true threads.

David

Would I be using Socket_TCP then?[/url]

No, you have to go a little higher than that. You would be using Connection, ConnectionManager, ConnectionWriter, ConnectionReader, and so on.

These classes are briefly described in the Panda3D manual under Networking. I believe there are many examples in the forum as well.

David

I use asyncore (it uses socket too), and its loop() is also blocking.
I worked around it by calling it every frame in a task, but using 0 timeout and 1 poll count.
Works perfectly and still simple.

I am trying to use panda’s connections still using google protocol buffers as the medium. I set the reader and writer to raw. I can send strings just fine, however receiving raw strings is still an issue. ConnectionReader.dataAvailiable() returns true, but getData just returns an empty string.

# -*- coding: utf-8 -*-
"""
Network stuffs
"""

import protocol_pb2 as proto
from direct.showbase import DirectObject
from direct.stdpy import threading

from pandac.PandaModules import QueuedConnectionManager, QueuedConnectionReader, ConnectionWriter, QueuedConnectionListener

# Networking
import socket

class ServerSocket(threading.Thread, DirectObject.DirectObject):
    """
    Connection to client
    """
    def __init__(self):
        """
        Overide to threading for client socket
        self.buffer:    Buffer string for incoming messages
        """
        self.cManager = QueuedConnectionManager()
        self.cReader = QueuedConnectionReader(self.cManager,0)
        self.cReader.setRawMode(True)
        self.cWriter = ConnectionWriter(self.cManager,0)
        self.cWriter.setRawMode(True)
        self.cListener = QueuedConnectionListener(self.cManager, 0)
        self.connection = None
        
        self.buffer = ""
        self.accept("exit", self.exit)
        self.accept("connect", self.connect)
        self.accept("sendData", self.send)
        threading.Thread.__init__(self)
    
    def connect(self, host, userName, userPassword):
        """
        connects with the server
        """
        # Fire login data!
        self.myConnection=  self.cManager.openTCPClientConnection(host,52003,3000)
        self.cReader.addConnection(self.myConnection)
        
        container = proto.Container()
        container.login.name = userName
        container.login.password = userPassword
        container.login.regionPassword = ""
        self.send(container)
        self.start()
        
    def run(self):
        self.running = True
        import time
        while self.running:
            d = ""
            print "Data?", self.cReader.dataAvailable()
            time.sleep(1)
            if self.cReader.dataAvailable():
                v = self.cReader.getData(d)
                print "I gots:", v, d
                self.buffer += d
            # We now check for the end tag "[!]" and fire off the appropriate serialized string
            if "[!]" in self.buffer:
                # Only one string is brought out at a time
                # What ever partial or complete message is left is put back into the buffer for the next cycle
                # This is because I am lazy
                data, self.buffer = self.buffer.split("[!]", 1)
                self.processData(data)
    
    def processData(self, data):
        """
        Processes network communication into engine events
        Some responses do not need any additional processing
        so we respond right away
        """
        # Stuff goes here
    
    def send(self, data):
        """
        sends data to client
        data needs to be a Protocol Buffer Container object
        """
        try:
            print "Sending Data:", str(data)[0:100]
            self.cWriter.send(data.SerializeToString(),self.myConnection)
        except:
            raise
            print "Object is not a protocol buffer object or is missing a parameter."
    
    def exit(self):
        self.s.close()
        self.running = False

self.client.setblocking(0) wont work?

Hmm, I’m not sure how well the threaded read mechanism works in raw mode. I’ll have to investigate.

David

I switched to polling, however considerYield does not exist in threading.Thread (is “from direct.stdpy import threading” correct?). Is this not in 1.6.2 or am I totally missing something?

No, sorry, this is PandaModules.Thread, a different class. The Thread class in stdpy.threading is designed to emulate Python’s Thread class, which doesn’t have that interface. But it’s probably not a bad idea to add it, for convenience. I’ll do that for 1.7.0. In the meantime, just use PandaModules.Thread.considerYield instead.

David