Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. PyQt5 - TCP readyRead only firing once if server is on its own QThread
Forum Updated to NodeBB v4.3 + New Features

PyQt5 - TCP readyRead only firing once if server is on its own QThread

Scheduled Pinned Locked Moved Unsolved Qt for Python
14 Posts 2 Posters 1.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • I Igor86

    @JonB The client is Hercules, and am sending data there.. here is a screenshot of what happened when i tried to send:

    b521cfcc-8a95-459e-9e58-072072af0f9e-image.png

    The first "STA,1" is received and "OK" is answered by the server. all successive messages are without answer.

    I Offline
    I Offline
    Igor86
    wrote on last edited by
    #4

    Just to elaborate, my python program is the TCP SERVER. my client is another program.
    all 3 files posted above are fom my server. main.py (main thread), server.py (tcp server on its own thread), client.py (client handling of the server). there is no more code involved other than this.

    so when my external application connects, it is able to send mesages and stay connected. the server does only detect the first packet tough.. then it ignores the rest. The connection is not dropped, the client is still able to send data, but the server is not evaluating it / firing readyRead.

    1 Reply Last reply
    0
    • I Igor86

      @JonB The client is Hercules, and am sending data there.. here is a screenshot of what happened when i tried to send:

      b521cfcc-8a95-459e-9e58-072072af0f9e-image.png

      The first "STA,1" is received and "OK" is answered by the server. all successive messages are without answer.

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #5

      @Igor86
      You say nothing about when your client sends the 3 STA messages. You do not say if it waits for the OK before sending the next one.

      If it sends multiple messages without waiting for the server's response then your code in on_ready_read() will/can only pick up the first one and send the OK acknowledgment. Is this your case? You would need to look through all the data received from readAll() (and presumably acknowledge each one, if that is what you want).

      The above has nothing to do with whether you have secondary threads or just one thread --- it is always the case.

      Your code also has a deeper assumption which is not valid, even if the client does wait for server's OK before sending another message. It assumes that on_ready_read()/readAll() will be called/collect a "complete" message, such as STA,1. But it may not: it might only receive the first character, S, on one call, and then it might receive TA,1 in another message. Your code does not accommodate that, and because you simply have an if segments[0] == "STA" it will neither respond not print anything out in that case, so you won't even know.....

      I 1 Reply Last reply
      2
      • JonBJ JonB

        @Igor86
        You say nothing about when your client sends the 3 STA messages. You do not say if it waits for the OK before sending the next one.

        If it sends multiple messages without waiting for the server's response then your code in on_ready_read() will/can only pick up the first one and send the OK acknowledgment. Is this your case? You would need to look through all the data received from readAll() (and presumably acknowledge each one, if that is what you want).

        The above has nothing to do with whether you have secondary threads or just one thread --- it is always the case.

        Your code also has a deeper assumption which is not valid, even if the client does wait for server's OK before sending another message. It assumes that on_ready_read()/readAll() will be called/collect a "complete" message, such as STA,1. But it may not: it might only receive the first character, S, on one call, and then it might receive TA,1 in another message. Your code does not accommodate that, and because you simply have an if segments[0] == "STA" it will neither respond not print anything out in that case, so you won't even know.....

        I Offline
        I Offline
        Igor86
        wrote on last edited by Igor86
        #6

        @JonB thank you for your answer. The sta,1 are sent by me manually.. there were a few seconds between them.

        It is repeatable that it replies "ok" only to the first message. closing and reopening the connection fixes the problem.

        i also added a print to beginning of the readReady and it only printed once. so indeed it is not firing anymore after the first time.

        This did not happen before i movedi it to its own thread. it worked...

        So I agree on the points you made about the assumptions I make in the code, but i strongly believe that this is not the cause of the problem...

        In fact, we can remove the whole "if" condition and simply write back "ok" whenever ANYTHING arrives, and it behaves the same

        JonBJ 1 Reply Last reply
        0
        • I Igor86

          @JonB thank you for your answer. The sta,1 are sent by me manually.. there were a few seconds between them.

          It is repeatable that it replies "ok" only to the first message. closing and reopening the connection fixes the problem.

          i also added a print to beginning of the readReady and it only printed once. so indeed it is not firing anymore after the first time.

          This did not happen before i movedi it to its own thread. it worked...

          So I agree on the points you made about the assumptions I make in the code, but i strongly believe that this is not the cause of the problem...

          In fact, we can remove the whole "if" condition and simply write back "ok" whenever ANYTHING arrives, and it behaves the same

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by
          #7

          @Igor86
          Let's assume you are correct. Then, I don't know, does the server-separate-thread continue to service a message loop for messages after the first?

          You do not try to access (read or write) the socket from different threads, do you?

          I think you should set up a tiny, standalone example with your separate-thread server to test.

          I 1 Reply Last reply
          0
          • JonBJ JonB

            @Igor86
            Let's assume you are correct. Then, I don't know, does the server-separate-thread continue to service a message loop for messages after the first?

            You do not try to access (read or write) the socket from different threads, do you?

            I think you should set up a tiny, standalone example with your separate-thread server to test.

            I Offline
            I Offline
            Igor86
            wrote on last edited by Igor86
            #8

            @JonB thank you, I just made a minimal reproducible test, and this is what got printed out:

            QObject: Cannot create children for a parent that is in a different thread.
            (Parent is QNativeSocketEngine(0x25e41138b70), parent's thread is QThread(0x25e3e9ce3f0), current thread is QThread(0x25e40d34970)

            what is the meaning of this?

            here is the sample code: https://igormasin.it/fileuploads/tcptest.zip

            JonBJ 1 Reply Last reply
            0
            • I Igor86

              @JonB thank you, I just made a minimal reproducible test, and this is what got printed out:

              QObject: Cannot create children for a parent that is in a different thread.
              (Parent is QNativeSocketEngine(0x25e41138b70), parent's thread is QThread(0x25e3e9ce3f0), current thread is QThread(0x25e40d34970)

              what is the meaning of this?

              here is the sample code: https://igormasin.it/fileuploads/tcptest.zip

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #9

              @Igor86
              That's the whole point! (But now not sure if that is the case with your original code you did not get the same error there?)

              When using threads exactly as it says Qt does not allow QObjects to be created parented to a QObject in another thread.

              here is the sample code: https://igormasin.it/fileuploads/tcptest.zip

              Others may download/look at an external file, but not me. If the code isn't small enough to paste it's (probably) not a minimal, reproducible example.

              I 1 Reply Last reply
              0
              • JonBJ JonB

                @Igor86
                That's the whole point! (But now not sure if that is the case with your original code you did not get the same error there?)

                When using threads exactly as it says Qt does not allow QObjects to be created parented to a QObject in another thread.

                here is the sample code: https://igormasin.it/fileuploads/tcptest.zip

                Others may download/look at an external file, but not me. If the code isn't small enough to paste it's (probably) not a minimal, reproducible example.

                I Offline
                I Offline
                Igor86
                wrote on last edited by
                #10

                @JonB yes the message is shown there too, but it was logged as a info and not as a error by mistake so it was not displayed..

                But how can i solve this? do i have to move the code from the client.py to server.py?

                sure i can post the code here..

                here we go:

                main.py:

                import sys
                import server
                from PyQt5 import QtWidgets, QtCore
                from PyQt5.QtCore import *
                from PyQt5.QtGui import *
                from PyQt5.QtWidgets import *
                from PyQt5.uic import loadUi
                
                
                class UiMainWindow(QMainWindow):
                
                    def __init__(self):
                        super(UiMainWindow, self).__init__()
                    
                        # server
                        self.serverThread = QThread()
                        self.serverWorker = server.Server()
                        self.serverWorker.moveToThread(self.serverThread)
                        self.serverThread.start()
                        #self.serverThread.start_server()
                        self.show()
                
                if __name__ == '__main__':
                    app = QtWidgets.QApplication(sys.argv)
                    MainWindow = QtWidgets.QMainWindow()
                    ui = UiMainWindow()
                    sys.exit(app.exec_())
                
                

                server.py

                import sys
                from PyQt5.QtCore import QObject
                from PyQt5.QtNetwork import QHostAddress, QTcpServer
                import client
                
                
                class Server(QObject):
                    def __init__(self):
                        QObject.__init__(self)
                        self.TCP_LISTEN_TO_PORT = 2001
                        self.server = QTcpServer()
                        self.server.newConnection.connect(self.on_new_connection)
                        self.client = client.Client(self)
                        self.start_server()
                
                    def __exit__(self, exc_type, exc_val, exc_tb):
                        print("server exited..")
                
                    def on_new_connection(self):
                        while self.server.hasPendingConnections():
                            self.client.set_socket(self.server.nextPendingConnection())
                
                    def start_server(self):
                        if self.server.listen(
                                QHostAddress.Any, self.TCP_LISTEN_TO_PORT
                        ):
                            print("Server is listening on port: {}".format(self.TCP_LISTEN_TO_PORT))
                        else:
                            print("Server couldn't wake up")
                

                client.py:

                import sys
                
                from PyQt5.QtCore import *
                
                
                def on_connected():
                    print("Client connected")
                
                
                def on_disconnected():
                    print("Client disconnected")
                
                
                class Client(QObject):
                
                    def __init__(self, parent=None):
                        super().__init__(parent)
                        self.socket = None
                
                
                    def set_socket(self, socket):
                        self.socket = socket
                        self.socket.connected.connect(on_connected)
                        self.socket.disconnected.connect(on_connected)
                        self.socket.readyRead.connect(self.on_ready_read)
                        print("Client connected from Ip %s" % self.socket.peerAddress().toString())
                
                    def on_ready_read(self):
                        msg = self.socket.readAll()
                        msg_txt = str(msg, 'utf-8').strip()
                        messages = msg_txt.split("\r")
                        segments = messages[0].split(",")
                        
                
                        if segments[0] == "STA":
                            status = int(segments[1])
                            # send back OK message
                            out = 'OK\r'
                            self.socket.write(bytearray(out, 'ascii'))
                
                
                JonBJ 1 Reply Last reply
                0
                • I Offline
                  I Offline
                  Igor86
                  wrote on last edited by
                  #11

                  Fixed it! thank you for your precious help!

                  1 Reply Last reply
                  0
                  • I Igor86

                    @JonB yes the message is shown there too, but it was logged as a info and not as a error by mistake so it was not displayed..

                    But how can i solve this? do i have to move the code from the client.py to server.py?

                    sure i can post the code here..

                    here we go:

                    main.py:

                    import sys
                    import server
                    from PyQt5 import QtWidgets, QtCore
                    from PyQt5.QtCore import *
                    from PyQt5.QtGui import *
                    from PyQt5.QtWidgets import *
                    from PyQt5.uic import loadUi
                    
                    
                    class UiMainWindow(QMainWindow):
                    
                        def __init__(self):
                            super(UiMainWindow, self).__init__()
                        
                            # server
                            self.serverThread = QThread()
                            self.serverWorker = server.Server()
                            self.serverWorker.moveToThread(self.serverThread)
                            self.serverThread.start()
                            #self.serverThread.start_server()
                            self.show()
                    
                    if __name__ == '__main__':
                        app = QtWidgets.QApplication(sys.argv)
                        MainWindow = QtWidgets.QMainWindow()
                        ui = UiMainWindow()
                        sys.exit(app.exec_())
                    
                    

                    server.py

                    import sys
                    from PyQt5.QtCore import QObject
                    from PyQt5.QtNetwork import QHostAddress, QTcpServer
                    import client
                    
                    
                    class Server(QObject):
                        def __init__(self):
                            QObject.__init__(self)
                            self.TCP_LISTEN_TO_PORT = 2001
                            self.server = QTcpServer()
                            self.server.newConnection.connect(self.on_new_connection)
                            self.client = client.Client(self)
                            self.start_server()
                    
                        def __exit__(self, exc_type, exc_val, exc_tb):
                            print("server exited..")
                    
                        def on_new_connection(self):
                            while self.server.hasPendingConnections():
                                self.client.set_socket(self.server.nextPendingConnection())
                    
                        def start_server(self):
                            if self.server.listen(
                                    QHostAddress.Any, self.TCP_LISTEN_TO_PORT
                            ):
                                print("Server is listening on port: {}".format(self.TCP_LISTEN_TO_PORT))
                            else:
                                print("Server couldn't wake up")
                    

                    client.py:

                    import sys
                    
                    from PyQt5.QtCore import *
                    
                    
                    def on_connected():
                        print("Client connected")
                    
                    
                    def on_disconnected():
                        print("Client disconnected")
                    
                    
                    class Client(QObject):
                    
                        def __init__(self, parent=None):
                            super().__init__(parent)
                            self.socket = None
                    
                    
                        def set_socket(self, socket):
                            self.socket = socket
                            self.socket.connected.connect(on_connected)
                            self.socket.disconnected.connect(on_connected)
                            self.socket.readyRead.connect(self.on_ready_read)
                            print("Client connected from Ip %s" % self.socket.peerAddress().toString())
                    
                        def on_ready_read(self):
                            msg = self.socket.readAll()
                            msg_txt = str(msg, 'utf-8').strip()
                            messages = msg_txt.split("\r")
                            segments = messages[0].split(",")
                            
                    
                            if segments[0] == "STA":
                                status = int(segments[1])
                                # send back OK message
                                out = 'OK\r'
                                self.socket.write(bytearray(out, 'ascii'))
                    
                    
                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by
                    #12

                    @Igor86 said in PyQt5 - TCP readyRead only firing once if server is on its own QThread:

                    but it was logged as a info and not as a error by mistake so it was not displayed..

                    Grrrr :)

                    self.client.set_socket(self.server.nextPendingConnection())

                    server is running in its own QThread, but it passes a socket to client which is running in the main thread. Not allowed!

                    Fixed it! thank you for your precious help!

                    You have just replied with this while I was typing. Good :)

                    I 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @Igor86 said in PyQt5 - TCP readyRead only firing once if server is on its own QThread:

                      but it was logged as a info and not as a error by mistake so it was not displayed..

                      Grrrr :)

                      self.client.set_socket(self.server.nextPendingConnection())

                      server is running in its own QThread, but it passes a socket to client which is running in the main thread. Not allowed!

                      Fixed it! thank you for your precious help!

                      You have just replied with this while I was typing. Good :)

                      I Offline
                      I Offline
                      Igor86
                      wrote on last edited by Igor86
                      #13

                      @JonB sorry for bothering again, but i cant figure this out. I tried to put both files (server.py and client.py) toghether into the same file. its my understanding that this runs all on the same thread, in the server class.. but i am obviously wrong, since i get the same error as above again.. what am i missing? main.py hasn't changed. just brought toghether server and client into server so it makes more sense..

                      import sys
                      from PyQt5.QtCore import *
                      from PyQt5.QtNetwork import QHostAddress, QTcpServer
                      
                      
                          
                      class Server(QObject):
                          def __init__(self):
                              QObject.__init__(self)
                              self.TCP_LISTEN_TO_PORT = 2001
                              self.server = QTcpServer()
                              self.server.newConnection.connect(self.on_new_connection)
                              self.start_server()
                              self.socket = None
                      
                          def __exit__(self, exc_type, exc_val, exc_tb):
                              print("server exited..")
                      
                          def on_new_connection(self):
                              while self.server.hasPendingConnections():
                                  self.set_socket(self.server.nextPendingConnection())
                      
                          def start_server(self):
                              if self.server.listen(
                                      QHostAddress.Any, self.TCP_LISTEN_TO_PORT
                              ):
                                  print("Server is listening on port: {}".format(self.TCP_LISTEN_TO_PORT))
                              else:
                                  print("Server couldn't wake up")
                                  
                          def set_socket(self, socket):
                              self.socket = socket
                              self.socket.connected.connect(self.on_connected)
                              self.socket.disconnected.connect(self.on_disconnected)
                              self.socket.readyRead.connect(self.on_ready_read)
                              print("Client connected from Ip %s" % self.socket.peerAddress().toString())
                      
                          def on_ready_read(self):
                              msg = self.socket.readAll()
                              msg_txt = str(msg, 'utf-8').strip()
                              messages = msg_txt.split("\r")
                              segments = messages[0].split(",")
                              
                      
                              if segments[0] == "STA":
                                  status = int(segments[1])
                                  # send back OK message
                                  out = 'OK\r'
                                  self.socket.write(bytearray(out, 'ascii'))
                                  
                          def on_connected(self):
                              print("Client connected")
                      
                      
                          def on_disconnected(self):
                              print("Client disconnected")
                      
                      
                      JonBJ 1 Reply Last reply
                      0
                      • I Igor86

                        @JonB sorry for bothering again, but i cant figure this out. I tried to put both files (server.py and client.py) toghether into the same file. its my understanding that this runs all on the same thread, in the server class.. but i am obviously wrong, since i get the same error as above again.. what am i missing? main.py hasn't changed. just brought toghether server and client into server so it makes more sense..

                        import sys
                        from PyQt5.QtCore import *
                        from PyQt5.QtNetwork import QHostAddress, QTcpServer
                        
                        
                            
                        class Server(QObject):
                            def __init__(self):
                                QObject.__init__(self)
                                self.TCP_LISTEN_TO_PORT = 2001
                                self.server = QTcpServer()
                                self.server.newConnection.connect(self.on_new_connection)
                                self.start_server()
                                self.socket = None
                        
                            def __exit__(self, exc_type, exc_val, exc_tb):
                                print("server exited..")
                        
                            def on_new_connection(self):
                                while self.server.hasPendingConnections():
                                    self.set_socket(self.server.nextPendingConnection())
                        
                            def start_server(self):
                                if self.server.listen(
                                        QHostAddress.Any, self.TCP_LISTEN_TO_PORT
                                ):
                                    print("Server is listening on port: {}".format(self.TCP_LISTEN_TO_PORT))
                                else:
                                    print("Server couldn't wake up")
                                    
                            def set_socket(self, socket):
                                self.socket = socket
                                self.socket.connected.connect(self.on_connected)
                                self.socket.disconnected.connect(self.on_disconnected)
                                self.socket.readyRead.connect(self.on_ready_read)
                                print("Client connected from Ip %s" % self.socket.peerAddress().toString())
                        
                            def on_ready_read(self):
                                msg = self.socket.readAll()
                                msg_txt = str(msg, 'utf-8').strip()
                                messages = msg_txt.split("\r")
                                segments = messages[0].split(",")
                                
                        
                                if segments[0] == "STA":
                                    status = int(segments[1])
                                    # send back OK message
                                    out = 'OK\r'
                                    self.socket.write(bytearray(out, 'ascii'))
                                    
                            def on_connected(self):
                                print("Client connected")
                        
                        
                            def on_disconnected(self):
                                print("Client disconnected")
                        
                        
                        JonBJ Offline
                        JonBJ Offline
                        JonB
                        wrote on last edited by JonB
                        #14

                        @Igor86
                        I'm not the expert on this thread stuff --- precisely because it's nasty, and most Qt stuff can be done without secondary threads despite what newcomers seem to think --- but isn't the server object in the worker thread but the client object is back in the main thread? You need to read https://doc.qt.io/qt-6/threads-qobject.html. QThread *QObject::thread() const returns the thread in which the object lives, use that dotted around your use of server/client/set_socket() etc. to see which thread you are in compared against QThread::currentThread() (https://stackoverflow.com/questions/33280672/qthreadcurrentthread-vs-qobjectthread).

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved