Network Programming
The QtNetwork module is Qt's network layer: the QNetworkAccessManager (the HTTP client - the get, the post, the QNetworkRequest), the QTcpSocket (the raw TCP - the connectToHost, the read), and the QTcpServer (the listening socket). The model is the asynchronous: the QNetworkAccessManager is the QObject (the finished signal on the response, the readyRead on the socket's data), and the network I/O is the event-loop's (the app.exec dispatches the signals, the worker-thread pattern the threads chapter covered is the same). This chapter covers the QNetworkAccessManager (the QNetworkRequest, the get/post, the finished signal, the QNetworkReply), the QTcpSocket (the client - the connectToHost, the readyRead, the readAll), and the QTcpServer (the server - the listen, the newConnection).
The QNetworkAccessManager: The HTTP Client
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QLabel
import sys
app = QApplication(sys.argv)
# the MANAGER: the QObject (the the app's the http client)
manager = QNetworkAccessManager()
label = QLabel("waiting")
# the REQUEST: the QNetworkRequest (the the url + the
# the headers)
request = QNetworkRequest(QUrl("https://api.example.com/items"))
request.setHeader(QNetworkRequest.UserAgentHeader,
"NotePad/1.0")
# the the POST: the setHttpMethod + the the body
# request.setHttpMethod(QNetworkRequest.PostMethod)
# the GET: the manager.get (the the QNetworkReply)
# the the reply is ASYNCHRONOUS (the the signal, not the
# the return) - the the finished signal is the the
# the response's
def on_finished(reply):
# the the signal: the the reply (the the QNetworkReply)
if reply.error() != reply.NoError:
label.setText("error: " + reply.errorString())
return
data = reply.readAll().data().decode("utf-8")
label.setText("got: " + data[:80])
reply.deleteLater()
manager.finished.connect(on_finished)
reply = manager.get(request)
# the the get RETURNS the reply (the the handle), and the
# the finished SIGNAL is the the completion (the the
# the event loop's, the the worker-thread pattern is the
# the same: the the async, the the signal)
sys.exit(app.exec_())
The QNetworkAccessManager is the HTTP client: the QObject (the finished signal on the response), the get/post (the QNetworkRequest the url plus the headers), and the QNetworkReply (the response - the readAll the body, the error/errorString the fail, the deleteLater the cleanup). The model is the asynchronous: the manager.get(request) returns the reply (the handle), and the finished signal is the completion - the event loop dispatches it (the app.exec), and the on_finished slot reads the reply (the error first, the readAll the body). The QNetworkRequest is the request: the QUrl the target, the setHeader the headers (the UserAgentHeader), the setHttpMethod the verb (the PostMethod), and the post the QByteArray body. The network I/O is the event-loop's: the finished signal is the completion, the readyRead (the socket) is the data, and the worker-thread pattern the threads chapter covered is the same (the async, the signal) - the HTTP is the QNetworkAccessManager, the raw TCP the next section.
The QTcpSocket: The Client and the Server
from PyQt5.QtNetwork import QTcpSocket, QTcpServer
from PyQt5.QtCore import QHostAddress
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
# the CLIENT: the QTcpSocket (the the connection)
client = QTcpSocket()
# the CONNECT: the connectToHost (the the async, the the
# the connected signal is the the completion)
def on_connected():
print("the connected")
client.write(b"the hello from the client")
client.connected.connect(on_connected)
client.connectToHost("localhost", 9000)
# the DATA: the readyRead (the the data available), the
# the readAll (the the bytes)
def on_ready_read():
data = client.readAll().data()
print("the got:", data)
client.readyRead.connect(on_ready_read)
# the SERVER: the QTcpServer (the the listening)
server = QTcpServer()
def on_new_conn():
socket = server.nextPendingConnection()
# the the per-client socket (the the QTcpSocket)
def on_client_read():
print("the client sent:", socket.readAll().data())
socket.write(b"the pong")
socket.readyRead.connect(on_client_read)
server.newConnection.connect(on_new_conn)
server.listen(QHostAddress.LocalHost, 9000)
# the the listen (the the port), the the newConnection
# signal is the the per-client (the the nextPending
# Connection is the the QTcpSocket the the client's)
sys.exit(app.exec_())
The QTcpSocket is the raw TCP: the client (the connectToHost the async, the connected signal the completion, the write the bytes, the readyRead the data available, the readAll the bytes) and the server (the QTcpServer the listen on the port, the newConnection signal the per-client, the nextPendingConnection the QTcpSocket for the client). The model is the same as the HTTP: the asynchronous (the connected, the readyRead, the newConnection signals - the event loop dispatches them), and the write/readAll are the bytes (the protocol is the app's - the QTcpSocket is the byte stream, the framing is the app's). The QHostAddress is the address (the LocalHost, the QHostAddress(ip)), and the listen is the port (the QTcpServer the binding). The TCP is the raw (the byte stream, the protocol the app's), and the HTTP is the QNetworkAccessManager (the protocol built-in, the QNetworkReply the response) - the two are the network layer: the HTTP for the REST, the TCP for the custom protocol.
The Working Rules
- The network I/O is the event-loop's, the signal is the completion. The
QNetworkAccessManagerand theQTcpSocketare theQObject(thefinished, theconnected, thereadyRead, thenewConnectionsignals) - the event loop dispatches them, and the slot reads the result. The synchronous block (thewaitForReadyRead) is the rare case, not the pattern. - The
QNetworkRequestis the url plus the headers, thereplyis the response. ThesetHeader(theUserAgentHeader), thesetHttpMethod(thePostMethod), and thereply'serrorfirst (theerrorStringthe text), thereadAllthe body, thedeleteLaterthe cleanup. Thereplyis the handle theget/postreturns, and thefinishedsignal is the completion. - The
QTcpSocketis the byte stream, the framing is the app's. Thewritethe bytes, thereadAllthe available, and the protocol (the length prefix, the delimiter, the JSON) is the app's - theQTcpSocketis the transport, not the protocol. - The
QTcpServeris the listener, thenextPendingConnectionis the per-client. Thelistenon the port, thenewConnectionsignal the per-client, and thenextPendingConnectiontheQTcpSocketfor that client - the server's loop is thenewConnectionplus the per-client socket'sreadyRead. - The
QHostAddressis the address, thelistenis the port. TheLocalHostthe loopback, theQHostAddress(ip)the specific, and thelisten(address, port)the binding - the server's identity is the address plus the port, and the client'sconnectToHostis the same pair.
Summary
The QtNetwork module is Qt's network layer, and the model is the asynchronous: the QObject signals (the finished, the connected, the readyRead, the newConnection) are the completions the event loop dispatches, and the slot reads the result. The QNetworkAccessManager is the HTTP client: the QNetworkRequest (the QUrl, the setHeader, the setHttpMethod), the get/post (the reply the handle), and the finished signal (the reply's error first, the readAll the body, the deleteLater the cleanup). The QTcpSocket is the raw TCP client (the connectToHost the async, the connected the completion, the write/readAll the bytes), and the QTcpServer is the listener (the listen on the port, the newConnection the per-client, the nextPendingConnection the client's QTcpSocket). The QHostAddress is the address, the listen the port, and the TCP is the byte stream (the framing the app's) while the HTTP is the built-in protocol (the QNetworkReply the response) - the two are the network layer: the HTTP for the REST, the TCP for the custom protocol, and the event loop the dispatcher of both.