gpt4 book ai didi

python - PyQt QTcpServer : How to return data to multiple clients?

转载 作者:太空狗 更新时间:2023-10-30 00:41:40 24 4
gpt4 key购买 nike

我希望使用 PyQt 创建一个 QTcpServer,它可以同时向 2 个或更多客户端返回数据。我假设这将需要线程。

使用 threadedfortuneserver.py 示例作为测试用例(包含在 PyQt4 中,在我的系统中它位于/usr/share/doc/python-qt4-doc/examples/network),我想连接多个客户端和每次其中一位客户求一笔财富时,其他客户也会收到一条消息更新,例如“客户 X 刚刚收到财富‘blah blah blah’”。

我了解 fortuneserver/client 程序是如何工作的,但似乎在将 fortune 发送回客户端后客户端连接立即终止。我的具体问题是:

  1. 是否可以保持所有连接打开,以便每个当其中一位客户要求财富时,其他客户可以更新了吗?

  2. 如果是这样,跟踪和循环连接的客户端的最佳方法是什么?

这对我来说是一个严重的绊脚石,因为我想开发一个可以让多个客户端交互的应用程序,并且每个客户端都可以更新其他客户端的操作。

预先感谢您的帮助,如果我可以提供任何其他信息,请告诉我。

我找到了 this thread但没有足够的具体信息可供利用。其他讨论针对的是 Python 套接字包,但据我了解,在使用 PyQt 时,服务器应该是 QTcpServer,这样一切都很好。

*** 编辑 ***

这是我的解决方案的开始阶段。我已经创建了一个基本的服务器和客户端。服务器只是发回客户端在行编辑框中输入的内容。

我基于 Rapid GUI Programming with Python and Qt 第 18 章中的“建筑服务”示例.

我所做的主要更改是现在线程会无限期地运行并且它们的套接字保持打开状态,监听客户端发送的数据。

它可以很好地处理多个客户端。这当然很难看,但我认为这是一个很好的起点。

我想要的是能够在一个客户输入文本时通知每个客户(比如典型的聊天程序)。

此外,为了让您了解您正在与谁打交道,我不是专业程序员。我是一名物理学家,多年来一直在无纪律地编写脚本,并在我的腰带下摆弄。但我想尝试开发可以传递数据的基本服务器/客户端程序。

感谢任何帮助或建议!

服务器:

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

最佳答案

对于你们大多数人来说,显而易见的是,我并不完全理解如何处理线程!不用担心,我发现了一种设计服务器的方法,它可以将数据发送到多个客户端,而无需找到辅助线程。

真的很简单,但在最好的时候我不是最快的猫。

服务器:

#!/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 - PyQt QTcpServer : How to return data to multiple clients?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9355511/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com