QT network programming how to use QT6 framework QTcpServer and QTcpSocket network programming to realize variable length data packet sending and receiving: take user registration and login as an example to explain
Introduction
This article will introduce how to use the QT6 framework QTcpServer and QTcpSocket for network programming to realize variable-length data packet sending and receiving. This article uses the two basic functions of user registration and user login as examples to introduce QT6 network programming.
Table of contents
QT web server QTcpServer
QT network client QTcpSocket
Message Packet Type Definition
server implementation
client implementation
Socket base class ButianyunTcpSocket
test run
text
QT network server QTcpServer
QTcpServer is used to implement a TCP server. The virtual function incomingConnection can usually be overridden to create a custom type of network socket.
[virtual protected] void QTcpServer::incomingConnection(qintptr socketDescriptor)
After creating an object instance of this type, call the listen() function to start receiving connections from network clients.
QT network client QTcpSocket
Whether it is a TCP server or a TCP client, this type or a derived type will be used as the socket socket wrapper type.
This type provides the setSocketDescriptor() function to associate a socket descriptor with a wrapper class object instance.
[virtual] bool QAbstractSocket::setSocketDescriptor(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = ConnectedState, QIODeviceBase::OpenMode openMode = ReadWrite)
At the same time, the base class QAbstractSocket type of this type provides a series of functions to obtain the local IP address and port and the remote IP address and port.
Functions provided by the QAbstractSocket type to obtain local and remote IP addresses and ports
The QAbstactSocket type also provides a series of signals to know when the connection is established and when the connection is disconnected.
The QIODevice type, the base class of the QAbstractSocket type, provides a series of signals to know when there is new data to read and how much data has been written.
[signal] void QIODevice::readyRead() [signal] void QIODevice::bytesWritten(qint64 bytes)
The QIODevice type also provides some IO read and write functions.
Message packet type definition
The current requirement is to implement two functions: user registration and user login. The C++ code here is basically self-commenting, and it’s still easy to understand.
As a sample program, here only considers the relatively normal situation of a relatively safe local area network, and does not consider some complicated and abnormal situations in the public network situation, such as packet replay attacks, abnormal data packets, etc.
#ifndef BUTIANYUNMESSAGE_H #define BUTIANYUNMESSAGE_H #if defined(__cplusplus) extern "C" {<!-- --> #endif enum ButianyunMessageType {<!-- --> BMT_InvalidMessage, BMT_RegisterRequestMessage, BMT_RegisterResponseMessage, BMT_LoginRequestMessage, BMT_LoginResponseMessage }; struct ButianyunMessageHeader {<!-- --> unsigned short type; unsigned short result; unsigned int size; }; struct ButianyunRegisterRequestMessage {<!-- --> ButianyunMessageHeader header; char userid[32]; char password[32]; }; struct ButianyunRegisterResponseMessage {<!-- --> ButianyunMessageHeader header; }; struct ButianyunLoginRequestMessage {<!-- --> ButianyunMessageHeader header; char userid[32]; char password[32]; }; struct ButianyunLoginResponseMessage {<!-- --> ButianyunMessageHeader header; }; #if defined(__cplusplus) } #endif #define BUTIANYUN_MESSAGE_HEADER_SIZE sizeof(ButianyunMessageHeader) #endif // BUTIANYUNMESSAGE_H
Server implementation
Main program:
int main(int argc, char *argv[]) {<!-- --> QCoreApplication a(argc, argv); ButianyunTcpServer server; server.listen(QHostAddress("127.0.0.1"), 8888); return a.exec(); }
ButianyunTcpServer type:
This type derives a new type from the QTcpServer type, making it possible to create custom derived types of QTcpSocket that support specific functions.
The types are defined as follows:
#ifndef BUTIANYUNTCPSERVER_H #define BUTIANYUNTCPSERVER_H class ButianyunTcpServer : public QTcpServer {<!-- --> Q_OBJECT public: explicit ButianyunTcpServer(QObject *parent = nullptr); void incomingConnection(qintptr socket) override; }; #endif // BUTIANYUNTCPSERVER_H
The types are implemented as follows:
ButianyunTcpServer::ButianyunTcpServer(QObject *parent) : QTcpServer{<!-- -->parent} {<!-- --> } void ButianyunTcpServer::incomingConnection(qintptr socket) {<!-- --> ButianyunTcpSocket* connection = new ButianyunServerTcpSocket(this); connection->setSocketDescriptor(socket); addPendingConnection(connection); }
ButianyunTcpServerSocet type
The types are defined as follows:
#ifndef BUTIANYUNSERVERTCPSOCKET_H #define BUTIANYUNSERVERTCPSOCKET_H class ButianyunServerTcpSocket : public ButianyunTcpSocket {<!-- --> Q_OBJECT public: explicit ButianyunServerTcpSocket(QObject *parent = nullptr); protected: void handle_message(QByteArray & amp; data, const ButianyunMessageHeader & amp; header) override; private: void handle_message_register_request(QByteArray & amp; data); void handle_message_login_request(QByteArray & data); }; #endif // BUTIANYUNSERVERTCPSOCKET_H
The types are implemented as follows:
#include "butianyunservertcpsocket.h" #include "ButianyunMessage.h" ButianyunServerTcpSocket::ButianyunServerTcpSocket(QObject *parent) : ButianyunTcpSocket{<!-- -->parent} {<!-- --> } void ButianyunServerTcpSocket::handle_message(QByteArray & amp; data, const ButianyunMessageHeader & amp; header) {<!-- --> switch (header.type) {<!-- --> case BMT_RegisterRequestMessage: handle_message_register_request(data); break; case BMT_LoginRequestMessage: handle_message_login_request(data); break; default: break; } } void ButianyunServerTcpSocket::handle_message_register_request(QByteArray & amp; data) {<!-- --> const ButianyunRegisterRequestMessage* message = reinterpret_cast<const ButianyunRegisterRequestMessage*>(data.constData()); qInfo() << "register:" << message->userid << ", " << message->password; ButianyunRegisterResponseMessage response; response.header.type = BMT_RegisterResponseMessage; response.header.result = 0; response.header.size = sizeof(ButianyunRegisterResponseMessage); quint64 len = write(reinterpret_cast<const char*>( & amp; response), response. header. size); qInfo() << "register response sent."; } void ButianyunServerTcpSocket::handle_message_login_request(QByteArray & amp; data) {<!-- --> const ButianyunLoginRequestMessage* message = reinterpret_cast<const ButianyunLoginRequestMessage*>(data.constData()); qInfo() << "login:" << message->userid << ", " << message->password; ButianyunLoginResponseMessage response; response.header.type = BMT_LoginResponseMessage; response.header.result = 0; response.header.size = sizeof(ButianyunLoginResponseMessage); quint64 len = write(reinterpret_cast<const char*>( & amp; response), response. header. size); qInfo() << "login response sent."; }
Client implementation
Main program:
Only a few situations are tested here, including normal registration, normal login, abnormal login, slow login, etc., without considering abnormal situations such as incomplete packet attacks and only connecting but not sending packets.
void register_client() {<!-- --> ButianyunClientSocket* client = new ButianyunClientSocket(qApp); QObject::connect(client, &ButianyunClientSocket::connected, client, [client]{<!-- --> qInfo() << "connected to server."; qInfo() << "sending register request ..."; ButianyunRegisterRequestMessage msg; memset( &msg, 0, sizeof(ButianyunRegisterRequestMessage)); msg.header.type = BMT_RegisterRequestMessage; msg.header.result = 0; msg.header.size = sizeof(ButianyunRegisterRequestMessage); strncpy(msg. userid, "butianyun", sizeof("butianyun")); strncpy(msg.password, "butianyun", sizeof("butianyun")); qint64 len = client->write(reinterpret_cast<const char*>( & amp;msg), sizeof(ButianyunRegisterRequestMessage)); if (len < sizeof(ButianyunRegisterRequestMessage)) {<!-- --> qInfo() << "Failed to send regiser request:" << len; client->close(); client->deleteLater(); return; } qInfo() << "register request ok"; }); QObject::connect(client, &ButianyunClientSocket::sig_message_result, client, [client](unsigned short type, unsigned short result) {<!-- --> if (BMT_RegisterResponseMessage == type & amp; & amp; 0 == result) {<!-- --> qInfo() << "sending login request ..."; ButianyunLoginRequestMessage msg; memset( &msg, 0, sizeof(ButianyunLoginRequestMessage)); msg.header.type = BMT_LoginRequestMessage; msg.header.result = 0; msg.header.size = sizeof(ButianyunLoginRequestMessage); strncpy(msg. userid, "butianyun", sizeof("butianyun")); strncpy(msg.password, "butianyun", sizeof("butianyun")); qint64 len = client->write(reinterpret_cast<const char*>( & amp;msg), sizeof(ButianyunLoginRequestMessage)); if (len < sizeof(ButianyunLoginRequestMessage)) {<!-- --> qInfo() << "Failed to send login request:" << len; client->close(); client->deleteLater(); return; } qInfo() << "login request ok"; } }); client->connectToHost(QHostAddress("127.0.0.1"), 8888); } void login_client() {<!-- --> ButianyunClientSocket* client = new ButianyunClientSocket(qApp); QObject::connect(client, &ButianyunClientSocket::connected, client, [client]{<!-- --> qInfo() << "connected to server."; qInfo() << "sending login request ..."; ButianyunLoginRequestMessage msg; memset( &msg, 0, sizeof(ButianyunLoginRequestMessage)); msg.header.type = BMT_LoginRequestMessage; msg.header.result = 0; msg.header.size = sizeof(ButianyunLoginRequestMessage); strncpy(msg. userid, "butianyun", sizeof("butianyun")); strncpy(msg.password, "butianyun", sizeof("butianyun")); qint64 len = client->write(reinterpret_cast<const char*>( & amp;msg), sizeof(ButianyunLoginRequestMessage)); if (len < sizeof(ButianyunLoginRequestMessage)) {<!-- --> qInfo() << "Failed to send login request:" << len; client->close(); client->deleteLater(); return; } qInfo() << "login request ok"; }); client->connectToHost(QHostAddress("127.0.0.1"), 8888); } void slow_client() {<!-- --> ButianyunClientSocket* client = new ButianyunClientSocket(qApp); QObject::connect(client, &ButianyunClientSocket::connected, client, [client]{<!-- --> qInfo() << "connected to server."; qInfo() << "sending slow login request ..."; static ButianyunLoginRequestMessage msg; memset( &msg, 0, sizeof(ButianyunLoginRequestMessage)); msg.header.type = BMT_LoginRequestMessage; msg.header.result = 0; msg.header.size = sizeof(ButianyunLoginRequestMessage); strncpy(msg. userid, "butianyun", sizeof("butianyun")); strncpy(msg.password, "butianyun", sizeof("butianyun")); QTimer* timer = new QTimer(client); timer->setTimerType(Qt::CoarseTimer); timer->setInterval(1000); timer->setSingleShot(false); static int i = 0; QObject::connect(timer, &QTimer::timeout, client, [client, timer] {<!-- --> const char* p = reinterpret_cast<const char*>( & amp; msg); qint64 len = client->write(p + i, 1); if (len != 1) {<!-- --> qInfo() << "Failed to send login request:" << len << " : " << i; timer->stop(); timer->deleteLater(); client->close(); client->deleteLater(); return; } qInfo() << i << ": 1 byte of login message sent."; i + + ; if (i == sizeof(ButianyunLoginRequestMessage)) {<!-- --> qInfo() << "all bytes of login message sent."; timer->stop(); timer->deleteLater(); } }); timer->start(); }); client->connectToHost(QHostAddress("127.0.0.1"), 8888); } void abnormal_client() {<!-- --> ButianyunClientSocket* client = new ButianyunClientSocket(qApp); QObject::connect(client, &ButianyunClientSocket::connected, client, [client]{<!-- --> qInfo() << "connected to server."; qInfo() << "sending login request ..."; ButianyunLoginRequestMessage msg; memset( &msg, 0, sizeof(ButianyunLoginRequestMessage)); msg.header.type = BMT_LoginRequestMessage; msg.header.result = 0; msg.header.size = 5; //invalid packet! strncpy(msg. userid, "butianyun", sizeof("butianyun")); strncpy(msg.password, "butianyun", sizeof("butianyun")); qint64 len = client->write(reinterpret_cast<const char*>( & amp;msg), sizeof(ButianyunLoginRequestMessage)); if (len < sizeof(ButianyunLoginRequestMessage)) {<!-- --> qInfo() << "Failed to send login request:" << len; client->close(); client->deleteLater(); return; } qInfo() << "login request ok"; }); client->connectToHost(QHostAddress("127.0.0.1"), 8888); } int main(int argc, char *argv[]) {<!-- --> QCoreApplication a(argc, argv); register_client(); // login_client(); // slow_client(); // abnormal_client(); return a.exec(); }
ButianyunClientSocket type definition:
#ifndef BUTIANYUNCLIENTSOCKET_H #define BUTIANYUNCLIENTSOCKET_H #include "butianyuntcpsocket.h" class ButianyunClientSocket : public ButianyunTcpSocket {<!-- --> Q_OBJECT public: explicit ButianyunClientSocket(QObject *parent = nullptr); signals: void sig_message_result(unsigned short type, unsigned short result); protected: void handle_message(QByteArray & amp; data, const ButianyunMessageHeader & amp; header) override; private: void handle_message_register_response(QByteArray & data); void handle_message_login_response(QByteArray & data); }; #endif // BUTIANYUNCLIENTSOCKET_H
Type implementation:
#include "butianyunclientsocket.h" #include "ButianyunMessage.h" ButianyunClientSocket::ButianyunClientSocket(QObject *parent) : ButianyunTcpSocket{<!-- -->parent} {<!-- --> } void ButianyunClientSocket::handle_message(QByteArray & amp; data, const ButianyunMessageHeader & amp; header) {<!-- --> switch (header.type) {<!-- --> case BMT_RegisterResponseMessage: handle_message_register_response(data); break; case BMT_LoginResponseMessage: handle_message_login_response(data); break; default: break; } } void ButianyunClientSocket::handle_message_register_response(QByteArray & amp; data) {<!-- --> const ButianyunRegisterResponseMessage* message = reinterpret_cast<const ButianyunRegisterResponseMessage*>(data.constData()); qInfo() << "register result:" << (0 == message->header.result ? "OK" : "FAILED"); emit sig_message_result(message->header.type, message->header.result); } void ButianyunClientSocket::handle_message_login_response(QByteArray & amp; data) {<!-- --> const ButianyunLoginResponseMessage* message = reinterpret_cast<const ButianyunLoginResponseMessage*>(data.constData()); qInfo() << "login result:" << (0 == message->header.result ? "OK" : "FAILED"); emit sig_message_result(message->header.type, message->header.result); }
Socket base class ButianyunTcpSocket
This type implements the processing of half-packets and sticky packets that are common in network programming, so that it can support the transmission of variable-length data packets, and it can also support file transmission.
For the introduction of half bag and sticky bag, please refer to:
What is the half-packet and sticky-packet problem in TCP programming in QT network programming and how to solve it?
The types are defined as follows:
#ifndef BUTIANYUNTCPSOCKET_H #define BUTIANYUNTCPSOCKET_H class ButianyunMessageHeader; class ButianyunTcpSocket : public QTcpSocket {<!-- --> Q_OBJECT public: explicit ButianyunTcpSocket(QObject *parent = nullptr); protected: virtual void handle_message(QByteArray & amp; data, const ButianyunMessageHeader & amp; header); private slots: void slot_readReady(); private: int total_size; QByteArray bytes; }; #endif // BUTIANYUNTCPSOCKET_H
Type implementation:
ButianyunTcpSocket::ButianyunTcpSocket(QObject *parent) : QTcpSocket{<!-- -->parent} , total_size(0) {<!-- --> connect(this, & amp; ButianyunTcpSocket::readyRead, this, & amp; ButianyunTcpSocket::slot_readReady); connect(this, &ButianyunTcpSocket::connected, this, [this]{<!-- --> qInfo() << "connected: local:" << localAddress().toString() << ":" << localPort() << " , remote:" << peerAddress().toString() << ":" << peerPort(); }); connect(this, &ButianyunTcpSocket::disconnected, this, [this]{<!-- --> qInfo() << "disconnected: local:" << localAddress().toString() << ":" << localPort() << " , remote:" << peerAddress().toString() << ":" << peerPort(); deleteLater(); }); } void ButianyunTcpSocket::slot_readReady() {<!-- --> QByteArray tempbytes = readAll(); bytes.append(tempbytes); do {<!-- --> if (total_size == 0) {<!-- --> if (bytes. length() >= BUTIANYUN_MESSAGE_HEADER_SIZE) {<!-- --> const ButianyunMessageHeader* header = reinterpret_cast<const ButianyunMessageHeader*>(bytes.constData()); total_size = header->size; if (total_size < BUTIANYUN_MESSAGE_HEADER_SIZE) {<!-- --> qInfo() << "Invalid packet! total_size:" << total_size; close(); deleteLater(); return; } } } if (total_size >= BUTIANYUN_MESSAGE_HEADER_SIZE & amp; & amp; bytes.length() >= total_size) {<!-- --> const ButianyunMessageHeader* header = reinterpret_cast<const ButianyunMessageHeader*>(bytes.constData()); handle_message(bytes, *header); bytes. remove(0, header->size); total_size = 0; } } while (0 == total_size & amp; & amp; bytes. length() >= BUTIANYUN_MESSAGE_HEADER_SIZE); } void ButianyunTcpSocket::handle_message(QByteArray & amp; data, const ButianyunMessageHeader & amp; header) {<!-- --> qInfo() << "Message:" << header.type << " NOT handled"; }
Test run
server:
QT6 TCP server running results
client:
QT6 TCP client running results
Summarize
This article introduces how to use the QT6 framework QTcpServer and QTcpSocket for network programming to realize variable-length data packet sending and receiving. This article also introduces QT6 network programming by taking the two basic functions of user registration and user login as examples.
If you think this article is helpful to you, please like + like + bookmark immediately, and the author of this article will be able to start from your like + like + bookmark Get the motivation to create new and good articles from it. If you think the article written by the author has some reference value, you can also follow the author of this article.