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

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

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

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

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

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

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

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

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

*** РЕДАКТИРОВАТЬ ***

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

Я основываю это на примере «зданий» из главы 18 Rapid GUI Programming с Python и Qt .

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

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

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

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

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

SERVER:

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

    One Solution collect form web for “PyQt QTcpServer: как вернуть данные нескольким клиентам?”

    Как было, вероятно, очень очевидно для большинства из вас, я не совсем понял, как бороться с потоками! Чтобы не волноваться, я обнаружил способ разработки сервера, который может отправлять данные нескольким клиентам с первым вторичным потоком, который можно найти.

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

    SERVER:

     #!/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_() 

    КЛИЕНТ

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

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

    Мне было бы интересно узнать, что другие люди могут подумать об этом подходе. Ловушки, проблемы и т. Д.

    Благодаря!

    Python - лучший язык программирования в мире.