Szukam utworzyć QTcpServer za pomocą PyQt, który może jednocześnie zwracać dane do 2 lub więcej klientów. Zakładam, że będzie to wymagać wątkowania.PyQt QTcpServer: Jak zwrócić dane do wielu klientów?
Użycie przykładu threadedfortuneserver.py jako przypadku testowego (dołączonego do PyQt4, w moim systemie znajduje się w/usr/share/doc/python-qt4-doc/examples/network), chcę połączyć wielu klientów i za każdym razem, gdy jeden z klientów prosi o fortunę, inni klienci otrzymują również aktualną wiadomość: "Klient X właśnie otrzymał fortunę" bla bla bla ".
Rozumiem, jak działa program fortuneserver/client, ale wygląda na to, że połączenia z klientami są natychmiast kończone po wysłaniu fortuny do klienta. Moje konkretne pytania:
Czy to możliwe, aby utrzymać wszystkie połączenia otwarte tak, że każdy czas jeden z klientów żąda fortuny, pozostali klienci mogą być aktualizowane?
Jeśli tak, jaki jest najlepszy sposób śledzenia i pętli podłączonych klientów?
To poważna przeszkodą dla mnie, bo chcę się rozwijać aplikację, gdzie kilka klienci mogą wchodzić w interakcje, a każdy klient może być aktualizowana o działaniach innych klientów.
Z góry dziękuję za pomoc, daj mi znać, jeśli są jakieś inne informacje, które mogę podać.
Znalazłem this thread, ale nie było wystarczająco dużo konkretnych informacji, z których można skorzystać. Inne dyskusje dotyczyły pakietu gniazd Pythona, ale rozumiem, że podczas używania PyQt serwer powinien być serwerem QTcpServer, więc wszystko gra ładnie.
*** EDYCJA ***
Oto początkowe etapy mojego rozwiązania. Stworzyłem podstawowy serwer i klienta. Serwer po prostu odsyła z powrotem to, co klient wprowadził do pola edycji linii.
Opieram to na przykładzie "buildingservices" z rozdziału 18 z Rapid GUI Programming with Python and Qt.
Najważniejszą zmianą, którą wprowadziłem, jest to, że wątki działają nieprzerwanie, a ich gniazda pozostają otwarte, nasłuchując danych wysyłanych przez klienta.
Obsługuje wielu klientów dobrze. Jest to z pewnością brzydkie, ale uważam, że jest to dobry punkt wyjścia.
Chciałbym móc powiadomić każdego klienta, gdy tylko jeden klient wprowadzi tekst (na przykład typowy program do czatu).
Również, aby dać ci wyobrażenie, z kim masz do czynienia, NIE jestem profesjonalnym programistą. Jestem fizykiem, który ma wiele lat niezdyscyplinowanego pisania scenariuszy i błąkania się pod moim pasem. Ale chciałbym spróbować stworzyć podstawowe programy serwerowe/klienckie, które mogą przekazywać dane.
Dzięki za pomoc lub sugestie!
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_()
KLIENT:
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_()