Estoy buscando crear un QTcpServer usando PyQt que pueda devolver datos simultáneamente a 2 o más clientes. Supongo que esto requerirá enhebrar.PyQt QTcpServer: ¿Cómo devolver datos a múltiples clientes?
Usando el ejemplo de threadedfortuneserver.py como caso de prueba (incluido con PyQt4, en mi sistema se encuentra en/usr/share/doc/python-qt4-doc/examples/network), quiero conectar varios clientes y cada vez que uno de los clientes pide una fortuna, los otros clientes también se actualizan con un mensaje como "El cliente X acaba de recibir la fortuna 'blah blah blah'".
Entiendo cómo funciona el programa fortuneserver/client, pero parece que las conexiones con el cliente se cancelan inmediatamente después de que la fortuna se devuelve al cliente. Mis preguntas específicas son:
¿Es posible mantener todas las conexiones abiertas para que cada vez que uno de los clientes pide una fortuna, los otros clientes puede actualiza?
En caso afirmativo, ¿cuál es la mejor manera de realizar un seguimiento de los clientes conectados?
Este es un serio obstáculo para mí porque quiero desarrollar una aplicación donde pueden interactuar con varios clientes, y cada cliente puede ser informado sobre las acciones de los otros clientes.
Gracias de antemano por su ayuda, avíseme si hay alguna otra información que pueda proporcionar.
Encontré this thread pero no había suficiente información específica para hacer uso de. Otras discusiones han sido para el paquete de socket de Python, pero tengo entendido que cuando se utiliza PyQt, el servidor debe ser un QTcpServer para que todo funcione bien.
*** *** EDITAR
Aquí están las primeras etapas de mi solución. Creé un servidor y un cliente básico. El servidor simplemente devuelve lo que el cliente ingresó en un cuadro de edición de línea.
Estoy basando esto en el ejemplo "buildingservices" del Capítulo 18 de Rapid GUI Programming with Python and Qt.
El principal cambio que realicé es que ahora los hilos continúan ejecutándose indefinidamente y sus enchufes permanecen abiertos, escuchando los datos que envía el cliente.
Maneja múltiples clientes bien. Sin duda es feo, pero creo que es un buen punto de partida.
Lo que me gustaría es poder notificar a cada cliente cada vez que un cliente ingresa texto (como un programa de chat típico, por ejemplo).
Además, para darle una idea de con quién está tratando, NO soy un programador profesional. Soy un físico con muchos años de scripting indisciplinado y jugueteando bajo mi cinturón. Pero me gustaría tratar de desarrollar programas básicos de servidor/cliente que puedan transmitir datos.
¡Gracias por cualquier ayuda o sugerencia!
SERVIDOR:
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_()
CLIENTE:
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_()