PyQt5: Sending and receiving messages between client and server

Mahmoud Anwer picture Mahmoud Anwer · Dec 15, 2016 · Viewed 7k times · Source

I am trying to create a server and client to send and receive messages. My problem is to send and receive between the client and the server.

client.py:

from PyQt5.QtCore import QIODevice
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5.QtNetwork import QTcpSocket

class Client(QDialog):
    def __init__(self):
        super().__init__()
        HOST = '127.0.0.1'
        PORT = 8000
        self.tcpSocket = QTcpSocket(self)
        self.tcpSocket.connectToHost(HOST, PORT, QIODevice.ReadWrite)
        self.tcpSocket.readyRead.connect(self.dealCommunication)
        self.tcpSocket.error.connect(self.displayError)

    def dealCommunication(self):
        print("connected !\n")
        # i want here to send and receive messages

    def displayError(self):
        print(self, "The following error occurred: %s." % self.tcpSocket.errorString())

if __name__ == '__main__':

    import sys
    app = QApplication(sys.argv)
    client = Client()
    sys.exit(client.exec_())

server.py:

import sys
from PyQt5.QtCore import QByteArray, QDataStream, QIODevice
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5.QtNetwork import QHostAddress, QTcpServer, QTcpSocket

class Server(QDialog, QTcpSocket):
    def __init__(self):
        super().__init__()
        self.tcpServer = None

    def sessionOpened(self):
        self.tcpServer = QTcpServer(self)
        PORT = 8000
        address = QHostAddress('127.0.0.1')
        self.tcpServer.listen(address, PORT)
        self.tcpServer.newConnection.connect(self.dealCommunication)

    def dealCommunication(self):
        # i want here to send and receive messages 
        data = input()
        block = QByteArray()
        out = QDataStream(block, QIODevice.ReadWrite)
        out.writeQString(data)
        clientConnection = self.tcpServer.nextPendingConnection()
        clientConnection.write(block)
        clientConnection.disconnectFromHost()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    server = Server()
    server.sessionOpened()
    sys.exit(server.exec_())

I have searched but I am so confused. I found some code, but I can't understand what it does exactly:

data = input()
block = QByteArray()
out = QDataStream(block, QIODevice.ReadWrite)
out.writeQString(data)
clientConnection = self.tcpServer.nextPendingConnection()
clientConnection.write(block)
clientConnection.disconnectFromHost()

Answer

Dan-Dev picture Dan-Dev · Dec 16, 2016

client.py

from PyQt5.QtCore import QDataStream, QIODevice
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5.QtNetwork import QTcpSocket, QAbstractSocket

class Client(QDialog):
    def __init__(self):
        super().__init__()
        self.tcpSocket = QTcpSocket(self)
        self.blockSize = 0
        self.makeRequest()
        self.tcpSocket.waitForConnected(1000)
        # send any message you like it could come from a widget text.
        self.tcpSocket.write(b'hello')
        self.tcpSocket.readyRead.connect(self.dealCommunication)
        self.tcpSocket.error.connect(self.displayError)

    def makeRequest(self):
        HOST = '127.0.0.1'
        PORT = 8000
        self.tcpSocket.connectToHost(HOST, PORT, QIODevice.ReadWrite)

    def dealCommunication(self):
        instr = QDataStream(self.tcpSocket)
        instr.setVersion(QDataStream.Qt_5_0)
        if self.blockSize == 0:
            if self.tcpSocket.bytesAvailable() < 2:
                return
            self.blockSize = instr.readUInt16()
        if self.tcpSocket.bytesAvailable() < self.blockSize:
            return
        # Print response to terminal, we could use it anywhere else we wanted.
        print(str(instr.readString(), encoding='ascii'))

    def displayError(self, socketError):
        if socketError == QAbstractSocket.RemoteHostClosedError:
            pass
        else:
            print(self, "The following error occurred: %s." % self.tcpSocket.errorString())


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    client = Client()
    sys.exit(client.exec_())

server.py

import sys
from PyQt5.QtCore import QByteArray, QDataStream, QIODevice
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5.QtNetwork import QHostAddress, QTcpServer

class Server(QDialog):
    def __init__(self):
        super().__init__()
        self.tcpServer = None

    def sessionOpened(self):
        self.tcpServer = QTcpServer(self)
        PORT = 8000
        address = QHostAddress('127.0.0.1')
        if not self.tcpServer.listen(address, PORT):
            print("cant listen!")
            self.close()
            return
        self.tcpServer.newConnection.connect(self.dealCommunication)

    def dealCommunication(self):
        # Get a QTcpSocket from the QTcpServer
        clientConnection = self.tcpServer.nextPendingConnection()
        # instantiate a QByteArray
        block = QByteArray()
        # QDataStream class provides serialization of binary data to a QIODevice
        out = QDataStream(block, QIODevice.ReadWrite)
        # We are using PyQt5 so set the QDataStream version accordingly.
        out.setVersion(QDataStream.Qt_5_0)
        out.writeUInt16(0)
        # this is the message we will send it could come from a widget.
        message = "Goodbye!"
        # get a byte array of the message encoded appropriately.
        message = bytes(message, encoding='ascii')
        # now use the QDataStream and write the byte array to it.
        out.writeString(message)
        out.device().seek(0)
        out.writeUInt16(block.size() - 2)
        # wait until the connection is ready to read
        clientConnection.waitForReadyRead()
        # read incomming data
        instr = clientConnection.readAll()
        # in this case we print to the terminal could update text of a widget if we wanted.
        print(str(instr, encoding='ascii'))
        # get the connection ready for clean up
        clientConnection.disconnected.connect(clientConnection.deleteLater)
        # now send the QByteArray.
        clientConnection.write(block)
        # now disconnect connection.
        clientConnection.disconnectFromHost()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    server = Server()
    server.sessionOpened()
    sys.exit(server.exec_())

It comes with very limited error handling but shows you the basics.

Edited to explain the code you wanted explaining