PyQt QTcpServer: Как вернуть данные нескольким клиентам?

Я хочу создать QTcpServer с использованием PyQt, который может одновременно возвращать данные 2 или более клиентам. Я предполагаю, что для этого потребуется многопоточность.

Используя пример threadfortuneserver.py в качестве тестового примера (включенный в PyQt4, в моей системе он находится в / usr / share / doc / python-qt4-doc / examples / network), я хочу подключить несколько клиентов и каждый раз, когда один из клиентов просит состояние, другие клиенты также получают сообщение типа «Клиент X только что получил состояние« бла-бла-бла »».

Я понимаю, как работает программа «предсказатель / клиент», но мне кажется, что клиентские соединения немедленно прерываются после того, как состояние отправлено обратно клиенту.Мои конкретные вопросы:

  1. Можно ли сохранить все связи открытыми, чтобы каждый когда один из клиентов просит состояние, другие клиенты могут быть обновлено?

  2. Если да, то как лучше всего отслеживать и перебирать подключенных клиентов?

Это серьезный камень преткновения для меня, потому что я хочу разработать приложение, в котором могут взаимодействовать несколько клиентов, и каждый клиент может быть в курсе действий других клиентов.

Заранее благодарим за помощь. Дайте мне знать, если есть еще какая-нибудь информация, которую я могу предоставить.

Я нашел эту ветку , но не было достаточно конкретной информации для использования. Другие обсуждения касались пакета сокетов Python, но я понимаю, что при использовании PyQt сервер должен быть QTcpServer, чтобы все работало нормально.

*** EDIT ***

Вот начальные этапы моего решения. Я создал базовый сервер и клиент. Сервер просто отправляет обратно то, что клиент ввел в поле редактирования строки.

Я основываю это на примере "buildingservices" из главы 18 книги Быстрое программирование графического интерфейса пользователя с помощью Python и Qt .

Главное изменение, которое я сделал, заключается в том, что теперь потоки продолжают работать бесконечно, а их сокеты остаются открытыми, ожидая данных, отправляемых клиентом.

Он отлично справляется с несколькими клиентами. Это, конечно, некрасиво, но я думаю, что это хорошая отправная точка.

Я бы хотел иметь возможность уведомлять каждого клиента всякий раз, когда один из клиентов вводит текст (например, как в обычной программе чата).

Кроме того, чтобы дать вам представление о том, с кем вы имеете дело, я НЕ профессиональный программист. Я физик с долгими годами недисциплинированного написания сценариев и возни за плечами. Но я хотел бы попытаться разработать базовые серверные / клиентские программы, которые могут передавать данные.

Спасибо за любую помощь или предложения!

СЕРВЕР:

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_()

КЛИЕНТ:

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_()

5
задан Community 23 May 2017 в 12:26
поделиться