Question

Je cherche à créer un QTcpServer en utilisant PyQt qui peuvent simultanément renvoyer des données à 2 ou plusieurs clients. Je suppose que cela nécessitera un filetage.

En utilisant l'exemple threadedfortuneserver.py en cas de test (inclus avec PyQt4, sur mon système, il se trouve dans / usr / share / doc / python-qt4-doc / examples / réseau), je veux me connecter plusieurs clients et chacun temps des clients demande une fortune, les autres clients aussi soient mis à jour avec un message comme « client X vient de recevoir la fortune" bla bla bla ».

Je comprends le fonctionnement du programme fortuneserver / client, mais il semble que les connexions client sont terminées immédiatement après la fortune est envoyé au client. Mes questions spécifiques sont:

  1. Est-il possible de garder toutes les connexions ouvertes afin que chaque temps l'un des clients demande une fortune, les autres clients peuvent être mis à jour?

  2. Si oui, quelle est la meilleure façon de garder une trace de boucle et sur les clients connectés?

Ceci est une pierre d'achoppement grave pour moi parce que je veux développer une application où plusieurs clients peuvent interagir, et chaque client peut être mis à jour sur les actions des autres clients.

Merci d'avance pour votre aide, laissez-moi savoir s'il y a d'autres informations que je peux fournir.

J'ai trouvé ce fil mais il n'y avait pas suffisamment d'informations spécifiques d'utiliser. D'autres discussions ont été pour le paquet socket Python, mais je crois comprendre que lorsque vous utilisez PyQt, le serveur doit être un QTcpServer donc tout joue bien.

*** *** EDIT

Voici les premières étapes de ma solution. J'ai créé un serveur et le client de base. Le serveur renvoie uniquement ce que le client est entré dans une zone Modifier la ligne.

Je fonde cela sur l'exemple des « buildingservices » du chapitre 18 de GUI rapide de programmation avec Python et Qt .

Le principal changement que je fait est que maintenant les fils continuer à courir indéfiniment et leurs orbites restent ouverts, à l'écoute des données envoyées par le client.

Il gère plusieurs clients très bien. Il est certainement laid, mais je pense qu'il est un bon point de départ.

Ce que je voudrais est d'être en mesure d'informer chaque client chaque fois qu'un client entre dans le texte (comme un programme de chat typique, par exemple).

En outre, pour vous donner une idée de qui vous avez affaire, je ne suis pas un programmeur professionnel. Je suis un physicien avec de nombreuses années de script indisciplinés et bidouiller sous ma ceinture. Mais je voudrais essayer de développer des programmes client / serveur de base qui peut transmettre des données autour.

Merci pour toute aide ou suggestions!

SERVEUR:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtNetwork import *

PORT = 9999
SIZEOF_UINT16 = 2

class Thread(QThread):

    #lock = QReadWriteLock()

    def __init__(self, socketId, parent):
        super(Thread, self).__init__(parent)
        self.socketId = socketId

    def run(self):
        self.socket = QTcpSocket()

        if not self.socket.setSocketDescriptor(self.socketId):
            self.emit(SIGNAL("error(int)"), socket.error())
            return

        while self.socket.state() == QAbstractSocket.ConnectedState:
            nextBlockSize = 0
            stream = QDataStream(self.socket)
            stream.setVersion(QDataStream.Qt_4_2)
            if (self.socket.waitForReadyRead(-1) and
                self.socket.bytesAvailable() >= SIZEOF_UINT16):
                nextBlockSize = stream.readUInt16()
            else:
                self.sendError("Cannot read client request")
                return
            if self.socket.bytesAvailable() < nextBlockSize:
                if (not self.socket.waitForReadyRead(-1) or
                    self.socket.bytesAvailable() < nextBlockSize):
                    self.sendError("Cannot read client data")
                    return

            textFromClient = stream.readQString()

            textToClient = "You wrote: \"{}\"".format(textFromClient)
            self.sendReply(textToClient)

    def sendError(self, msg):
        reply = QByteArray()
        stream = QDataStream(reply, QIODevice.WriteOnly)
        stream.setVersion(QDataStream.Qt_4_2)
        stream.writeUInt16(0)
        stream.writeQString("ERROR")
        stream.writeQString(msg)
        stream.device().seek(0)
        stream.writeUInt16(reply.size() - SIZEOF_UINT16)
        self.socket.write(reply)

    def sendReply(self, text):
        reply = QByteArray()
        stream = QDataStream(reply, QIODevice.WriteOnly)
        stream.setVersion(QDataStream.Qt_4_2)
        stream.writeUInt16(0)
        stream.writeQString(text)
        stream.device().seek(0)
        stream.writeUInt16(reply.size() - SIZEOF_UINT16)
        self.socket.write(reply)


class TcpServer(QTcpServer):

    def __init__(self, parent=None):
        super(TcpServer, self).__init__(parent)

    def incomingConnection(self, socketId):
        self.thread = Thread(socketId, self)
        self.thread.start()


class ServerDlg(QPushButton):

    def __init__(self, parent=None):
        super(ServerDlg, self).__init__(
                "&Close Server", parent)
        self.setWindowFlags(Qt.WindowStaysOnTopHint)

        self.tcpServer = TcpServer(self)
        if not self.tcpServer.listen(QHostAddress("0.0.0.0"), PORT):
            QMessageBox.critical(self, "Threaded Server",
                    "Failed to start server: {}".format(
                    self.tcpServer.errorString()))
            self.close()
            return

        self.connect(self, SIGNAL("clicked()"), self.close)
        font = self.font()
        font.setPointSize(24)
        self.setFont(font)
        self.setWindowTitle("Threaded Server")

app = QApplication(sys.argv)
form = ServerDlg()
form.show()
form.move(0, 0)
app.exec_()

CLIENT:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtNetwork import *

PORT = 9999
SIZEOF_UINT16 = 2

class Form(QDialog):

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

        # Ititialize socket
        self.socket = QTcpSocket()
        # Initialize data IO variables
        self.nextBlockSize = 0
        self.request = None
        # Create widgets/layout
        self.browser = QTextBrowser()
        self.lineedit = QLineEdit("Texty bits")
        self.lineedit.selectAll()
        self.connectButton = QPushButton("Connect")
        self.connectButton.setDefault(False)
        self.connectButton.setEnabled(True)
        layout = QVBoxLayout()
        layout.addWidget(self.browser)
        layout.addWidget(self.lineedit)
        layout.addWidget(self.connectButton)
        self.setLayout(layout)
        self.lineedit.setFocus()

        # Signals and slots for line edit and connect button
        self.lineedit.returnPressed.connect(self.sendToServer)
        self.connectButton.released.connect(self.connectToServer)

        self.setWindowTitle("Client")

        # Signals and slots for networking
        self.socket.readyRead.connect(self.readFromServer)
        self.socket.disconnected.connect(self.serverHasStopped)
        self.connect(self.socket,
                     SIGNAL("error(QAbstractSocket::SocketError)"),
                     self.serverHasError)

    # Update GUI
    def updateUi(self, text):
        self.browser.append(text)

    # Create connection to server
    def connectToServer(self):
        self.connectButton.setEnabled(False)
        print("Connecting to server")
        self.socket.connectToHost("localhost", PORT)

    # Send data to server
    def sendToServer(self):
        self.request = QByteArray()
        stream = QDataStream(self.request, QIODevice.WriteOnly)
        stream.setVersion(QDataStream.Qt_4_2)
        stream.writeUInt16(0)
        stream.writeQString(self.lineedit.text())
        stream.device().seek(0)
        stream.writeUInt16(self.request.size() - SIZEOF_UINT16)
        self.socket.write(self.request)
        self.nextBlockSize = 0
        self.request = None
        self.lineedit.setText("")

    # Read data from server and update Text Browser
    def readFromServer(self):
        stream = QDataStream(self.socket)
        stream.setVersion(QDataStream.Qt_4_2)

        while True:
            if self.nextBlockSize == 0:
                if self.socket.bytesAvailable() < SIZEOF_UINT16:
                    break
                self.nextBlockSize = stream.readUInt16()
            if self.socket.bytesAvailable() < self.nextBlockSize:
                break
            textFromServer = stream.readQString()
            self.updateUi(textFromServer)
            self.nextBlockSize = 0

    def serverHasStopped(self):
        self.socket.close()

    def serverHasError(self):
        self.updateUi("Error: {}".format(
                self.socket.errorString()))
        self.socket.close()


app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
Était-ce utile?

La solution

Comme ce fut probablement exaspérante évident pour la plupart d'entre vous, je ne l'ai pas bien compris la façon de traiter avec des fils! Ne vous inquiétez pas, je l'ai découvert un moyen de concevoir un serveur qui peut envoyer des données à des clients multiples avec nary un fil secondaire à trouver.

Très simple, vraiment, mais je ne suis pas le plus rapide des chats au meilleur des cas.

SERVEUR:

#!/usr/bin/env python3

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtNetwork import *

PORT = 9999
SIZEOF_UINT32 = 4

class ServerDlg(QPushButton):

    def __init__(self, parent=None):
        super(ServerDlg, self).__init__(
                "&Close Server", parent)
        self.setWindowFlags(Qt.WindowStaysOnTopHint)

        self.tcpServer = QTcpServer(self)               
        self.tcpServer.listen(QHostAddress("0.0.0.0"), PORT)
        self.connect(self.tcpServer, SIGNAL("newConnection()"), 
                    self.addConnection)
        self.connections = []

        self.connect(self, SIGNAL("clicked()"), self.close)
        font = self.font()
        font.setPointSize(24)
        self.setFont(font)
        self.setWindowTitle("Server")

    def addConnection(self):
        clientConnection = self.tcpServer.nextPendingConnection()
        clientConnection.nextBlockSize = 0
        self.connections.append(clientConnection)

        self.connect(clientConnection, SIGNAL("readyRead()"), 
                self.receiveMessage)
        self.connect(clientConnection, SIGNAL("disconnected()"), 
                self.removeConnection)
        self.connect(clientConnection, SIGNAL("error()"), 
                self.socketError)

    def receiveMessage(self):
        for s in self.connections:
            if s.bytesAvailable() > 0:
                stream = QDataStream(s)
                stream.setVersion(QDataStream.Qt_4_2)

                if s.nextBlockSize == 0:
                    if s.bytesAvailable() < SIZEOF_UINT32:
                        return
                    s.nextBlockSize = stream.readUInt32()
                if s.bytesAvailable() < s.nextBlockSize:
                    return

                textFromClient = stream.readQString()
                s.nextBlockSize = 0
                self.sendMessage(textFromClient, 
                                 s.socketDescriptor())
                s.nextBlockSize = 0

    def sendMessage(self, text, socketId):
        for s in self.connections:
            if s.socketDescriptor() == socketId:
                message = "You> {}".format(text)
            else:
                message = "{}> {}".format(socketId, text)
            reply = QByteArray()
            stream = QDataStream(reply, QIODevice.WriteOnly)
            stream.setVersion(QDataStream.Qt_4_2)
            stream.writeUInt32(0)
            stream.writeQString(message)
            stream.device().seek(0)
            stream.writeUInt32(reply.size() - SIZEOF_UINT32)
            s.write(reply)

    def removeConnection(self):
        pass

    def socketError(self):
        pass


app = QApplication(sys.argv)
form = ServerDlg()
form.show()
form.move(0, 0)
app.exec_()

CLIENT

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtNetwork import *

PORTS = (9998, 9999)
PORT = 9999
SIZEOF_UINT32 = 4

class Form(QDialog):

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

        # Ititialize socket
        self.socket = QTcpSocket()

        # Initialize data IO variables
        self.nextBlockSize = 0
        self.request = None

        # Create widgets/layout
        self.browser = QTextBrowser()
        self.lineedit = QLineEdit("Enter text here, dummy")
        self.lineedit.selectAll()
        self.connectButton = QPushButton("Connect")
        self.connectButton.setEnabled(True)
        layout = QVBoxLayout()
        layout.addWidget(self.browser)
        layout.addWidget(self.lineedit)
        layout.addWidget(self.connectButton)
        self.setLayout(layout)
        self.lineedit.setFocus()

        # Signals and slots for line edit and connect button
        self.lineedit.returnPressed.connect(self.issueRequest)
        self.connectButton.clicked.connect(self.connectToServer)

        self.setWindowTitle("Client")
        # Signals and slots for networking
        self.socket.readyRead.connect(self.readFromServer)
        self.socket.disconnected.connect(self.serverHasStopped)
        self.connect(self.socket,
                     SIGNAL("error(QAbstractSocket::SocketError)"),
                     self.serverHasError)

    # Update GUI
    def updateUi(self, text):
        self.browser.append(text)

    # Create connection to server
    def connectToServer(self):
        self.connectButton.setEnabled(False)
        self.socket.connectToHost("localhost", PORT)

    def issueRequest(self):
        self.request = QByteArray()
        stream = QDataStream(self.request, QIODevice.WriteOnly)
        stream.setVersion(QDataStream.Qt_4_2)
        stream.writeUInt32(0)
        stream.writeQString(self.lineedit.text())
        stream.device().seek(0)
        stream.writeUInt32(self.request.size() - SIZEOF_UINT32)
        self.socket.write(self.request)
        self.nextBlockSize = 0
        self.request = None
        self.lineedit.setText("")

    def readFromServer(self):
        stream = QDataStream(self.socket)
        stream.setVersion(QDataStream.Qt_4_2)

        while True:
            if self.nextBlockSize == 0:
                if self.socket.bytesAvailable() < SIZEOF_UINT32:
                    break
                self.nextBlockSize = stream.readUInt32()
            if self.socket.bytesAvailable() < self.nextBlockSize:
                break
            textFromServer = stream.readQString()
            self.updateUi(textFromServer)
            self.nextBlockSize = 0

    def serverHasStopped(self):
        self.socket.close()
        self.connectButton.setEnabled(True)

    def serverHasError(self):
        self.updateUi("Error: {}".format(
                self.socket.errorString()))
        self.socket.close()
        self.connectButton.setEnabled(True)


app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()

En résumé, chaque connexion client ouvre un socket, et la prise est ajoutée à une liste de tous les sockets client. Puis, quand l'un des clients envoie le texte, les boucles de serveur sur les sockets client, trouve celui qui a bytesAvailable, il lit dans et envoie le message aux autres clients.

Je serais curieux d'entendre ce que les autres peuvent penser de cette approche. Pitfalls, questions, etc.

Merci!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top