我期待創建一個使用PyQt的QTcpServer,它可以同時將數據返回給2個或更多的客戶端。我認爲這將需要線程。使用threadedfortuneserver.py示例作爲測試用例(包含在PyQt4中,在我的系統上可以在/ usr/share/doc/python-qt4-doc/examples/network中找到),我想連接多個客戶端每當其中一位客戶要求發財時,其他客戶也會收到一條消息,例如「客戶X剛剛收到財富'等等等等'」。PyQt QTcpServer:如何將數據返回給多個客戶端?
我明白財富服務器/客戶端程序是如何工作的,但似乎客戶端連接在財富發送回客戶端後立即終止。我的具體問題是:
是否可以保持所有連接的開放,使每 一次客戶端的請求一個發財,其他客戶端可以 更新?
如果是這樣,跟蹤和循環連接的客戶端的最佳方式是什麼?
這是一個嚴重的絆腳石適合我,因爲我要開發一個應用程序,其中一些客戶端可以交互,和每個客戶端可以對其他客戶的行爲進行更新。
在此先感謝您的幫助,請告訴我是否有任何其他信息可以提供。
我發現this thread但沒有足夠的具體信息可供使用。其他的討論都是針對Python套接字包的,但我的理解是,當使用PyQt時,服務器應該是QTcpServer,所以一切都很好。
***編輯***
這是我的解決方案的開始階段。我創建了一個基本的服務器和客戶端。服務器只是將客戶端輸入的內容發送回行編輯框。
我基於第18章的「buildingservices」示例。
我所做的主要改變是,現在線程保持無限期運行,並且它們的套接字保持打開狀態,偵聽客戶端發送的數據。
它可以處理多個客戶端。這確實很難看,但我認爲這是一個很好的起點。
我希望能夠通知每個客戶端,每當一個客戶端輸入文本(如一個典型的聊天程序,說)。
此外,爲了讓您瞭解您與誰打交道,我不是專業程序員。我是一位物理學家,有着多年無紀律的腳本,並在我的腰帶上擺弄。但我想嘗試開發可傳遞數據的基本服務器/客戶端程序。
感謝您的任何幫助或建議!
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_()