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.5k 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
    19 May 2023, 09:53

    @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.

    J Offline
    J Offline
    JonB
    wrote on 19 May 2023, 10:41 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 19 May 2023, 10:56
    2
    • J JonB
      19 May 2023, 10:41

      @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 19 May 2023, 10:56 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

      J 1 Reply Last reply 19 May 2023, 11:03
      0
      • I Igor86
        19 May 2023, 10:56

        @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

        J Offline
        J Offline
        JonB
        wrote on 19 May 2023, 11:03 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 19 May 2023, 11:32
        0
        • J JonB
          19 May 2023, 11:03

          @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 19 May 2023, 11:32 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

          J 1 Reply Last reply 19 May 2023, 11:39
          0
          • I Igor86
            19 May 2023, 11:32

            @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

            J Offline
            J Offline
            JonB
            wrote on 19 May 2023, 11:39 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 19 May 2023, 11:42
            0
            • J JonB
              19 May 2023, 11:39

              @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 19 May 2023, 11:42 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'))
              
              
              J 1 Reply Last reply 19 May 2023, 11:58
              0
              • I Offline
                I Offline
                Igor86
                wrote on 19 May 2023, 11:53 last edited by
                #11

                Fixed it! thank you for your precious help!

                1 Reply Last reply
                0
                • I Igor86
                  19 May 2023, 11:42

                  @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'))
                  
                  
                  J Offline
                  J Offline
                  JonB
                  wrote on 19 May 2023, 11:58 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 19 May 2023, 12:21
                  0
                  • J JonB
                    19 May 2023, 11:58

                    @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 19 May 2023, 12:21 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")
                    
                    
                    J 1 Reply Last reply 19 May 2023, 12:34
                    0
                    • I Igor86
                      19 May 2023, 12:21

                      @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")
                      
                      
                      J Offline
                      J Offline
                      JonB
                      wrote on 19 May 2023, 12:34 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

                      14/14

                      19 May 2023, 12:34

                      • Login

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