2012-02-20 11 views
5

2つ以上のクライアントに同時にデータを返すことができるPyQtを使用してQTcpServerを作成しようとしています。私はこれがスレッドを必要とすると仮定します。PyQt QTcpServer:複数のクライアントにデータを返す方法は?

テストケース(PyQt4に含まれ、私のシステムでは/ usr/share/doc/python-qt4-doc/examples/networkにあります)としてのthreadedfortuneserver.pyの例を使用して、複数のクライアントクライアントの1つが幸運を求めるたびに、他のクライアントも「クライアントXはちょうど幸運を受け取ったようです」というメッセージで更新されます。

私はfortuneserver/clientプログラムの仕組みを理解していますが、その幸運がクライアントに送り返された後すぐにクライアント接続が終了したようです。私の具体的な質問は以下のとおりです。

  1. それは、クライアントの一つが幸運を要求し、すべての 時間は、他のクライアントが を更新することができるように、すべての接続が開いておくことは可能ですか?

  2. もしそうなら、接続されたクライアントを追跡してループする最良の方法は何ですか?

私はいくつかのクライアントが対話できるアプリケーションを開発したいので、これは重大な障害です。他のクライアントの動作については各クライアントを更新できます。

私が提供できる情報がある場合は、事前にお手数でお知らせください。

私はthis threadが見つかりましたが、使用するための十分な具体的な情報がありませんでした。 Pythonソケットパッケージに関しては他の議論がありましたが、PyQtを使用する場合、サーバーはQTcpServerでなければならないので、すべてがうまくいくと私は理解しています。

***編集***

私の解決策の最初の段階です。私は基本的なサーバーとクライアントを作成しました。サーバーは、クライアントがLine Editボックスに入力した内容を返信するだけです。

私はRapid GUI Programming with Python and Qtの18章の "buildingservices"の例に基づいています。

主な変更点は、スレッドが無期限に実行し続け、ソケットが開いたままで、クライアントから送信されたデータを受信することです。

複数のクライアントをうまく処理します。それは確かに醜いですが、私はそれが良い出発点だと思います。

私が望むのは、1人のクライアントがテキストを入力するたびに(通常のチャットプログラムのように)各クライアントに通知できることです。

また、あなたが誰と対処しているかを知るために、私はプロのプログラマーではありません。私は何年もの無制限のスクリプティングと私のベルトの下でバイバイをしている物理学者です。しかし、データを渡すことができる基本的なサーバー/クライアントプログラムを開発しようとしています。

ありがとうございました!

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

CLIENT:

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

答えて

8

として、私は完全にスレッドに対処する方法を理解していなかった、おそらくあなたのほとんどにexasperatingly明らかでした!心配しないで、私は、セカンダリスレッドが見つかるように複数のクライアントにデータを送信できるサーバを設計する方法を発見しました。

本当にシンプルで、本当に、私は時代の猫の中で最も早いわけではありません。

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

CLIENT

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

、各クライアント接続は、ソケットをオープンし、ソケットは、すべてのクライアントソケットのリストに追加されます。次に、クライアントの1つがテキストを送信すると、サーバーはクライアントソケットをループし、bytesAvailableを持つものを見つけて読み込み、他のクライアントにメッセージを送信します。

私は他の人がこのアプローチについて考えるかもしれないことを知りたいと思うでしょう。落とし穴、問題など

ありがとう!

関連する問題