2012-02-20 3 views
5

2 개 이상의 클라이언트에 동시에 데이터를 반환 할 수있는 PyQt를 사용하여 QTcpServer를 만들려고합니다. 나는 이것이 스레딩을 필요로한다고 가정한다.PyQt QTcpServer : 데이터를 여러 클라이언트에 반환하는 방법은 무엇입니까?

내 시스템의 PyQt4에 포함 된 threadedfortuneserver.py 예제를 테스트 케이스로 사용하여/usr/share/doc/python-qt4-doc/examples/network에 있습니다. 여러 클라이언트를 연결하려고합니다. 클라이언트 중 한 명이 재산을 요구할 때마다 다른 클라이언트는 "클라이언트 X가 방금받은 돈을 'ㅋㅋㅋ'와 같은 메시지로 업데이트합니다.

나는 fortuneserver/client 프로그램의 작동 방식을 이해하고 있지만, 운세가 클라이언트에 다시 전송 된 후 클라이언트 연결이 즉시 종료 된 것처럼 보입니다. 내 구체적인 질문은 다음과 같습니다

  1. 는 클라이언트 중 하나는 재산을 요청하는 모든 시간이, 다른 클라이언트가 업데이트 할 수 있도록 모든 연결이 열려 유지할 수 있습니까?

  2. 그렇다면 연결된 클라이언트를 추적하고 루프하는 가장 좋은 방법은 무엇입니까? 내가 여러 클라이언트가 상호 작용할 수있는 응용 프로그램을 개발하기를 원하기 때문에

이 나를 위해 심각한 걸림돌이며, 각 클라이언트는 다른 클라이언트의 행동에 대해 업데이트 할 수 있습니다.

미리 도움을 주셔서 감사합니다. 제공 할 수있는 다른 정보가 있으면 알려주십시오.

나는 this thread을 찾았지만 사용할 구체적인 정보가 충분하지 않았습니다. Python 소켓 패키지에 대한 다른 논의가 있었지만, PyQt를 사용할 때 서버가 QTcpServer 여야하므로 모든 것이 훌륭하게 작동한다는 것을 이해합니다.

*** 편집 ***

여기 내 해결책의 시작 단계입니다. 기본 서버와 클라이언트를 만들었습니다. 서버는 클라이언트가 라인 편집 상자에 입력 한 것을 다시 전송합니다.

저는 이것을 Rapid GUI Programming with Python and Qt의 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_() 

CLIENT :로는 아마 당신의 대부분을 exasperatingly 분명했다

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

, 나는 완전히 스레드를 처리하는 방법을 이해하지 못했다! 걱정하지 마라, 나는 찾을 수있는 2 차 스레드가있는 여러 클라이언트에 데이터를 보낼 수있는 서버를 설계하는 방법을 발견했다.

정말 간단하지만 실제로는 가장 좋은 시간에 가장 빠른 고양이는 아닙니다.

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

이 요약하려면, 각 클라이언트 연결은 소켓을 열고, 소켓은 모든 클라이언트 소켓의 목록에 추가됩니다. 그런 다음 클라이언트 중 하나가 텍스트를 보내면 서버는 클라이언트 소켓을 통해 루프를 실행하고 bytesAvailable이있는 클라이언트를 찾고 읽은 다음 다른 클라이언트로 메시지를 전송합니다.

다른 사람들이이 접근 방식에 대해 어떻게 생각하는지 궁금 할 것입니다. 함정, 문제 등

고마워!