Qt5 network programming 3-based on TCP programming

Qt5 network programming 3-based on TCP programming

1 TCP(transmission control protocol) working principle
TCP is a connection-oriented, reliable, byte stream-based transmission protocol. Before data transmission, the client and server must establish a connection. During the data transmission process, there is data verification. Therefore, when TCP transmits data, it compares Reliable, but not as efficient as UDP. In addition, it is a full-duplex way of working.
For a detailed description of TCP, you can refer to the blogger's blog: TCP data transmission process
2 Classes related to TCP programming in Qt
QTcpSocket Class: A class that provides a TCP socket, which can be used to establish TCP connections and transmit data streams. This class inherits from QAbstractSocket.

QTcpServer Class : Provides a class for a TCP server. This class makes it possible to receive an incoming TCP connection.
Call the listen() function to listen on a specified address, or all addresses. listen() listens for an incoming connection. Every time a client connects to the server, the newConnection() signal is fired.
nextPendingConnection() receives a pending connection and returns a pointer to a QTcpSocket object. You can use this object to interact with the client.
Call the close function to stop listening for incoming connections

3 TCP network chat room
3.1 Server side
1) Use QTcpServer to create a TCP server
2) Set the listening and IP port number
3) In response to the client's connection request, save the socket for communication with the client
4) Receive messages from clients in real time
5) Forward chat messages to other clients
3.2 Client
1) Use QTcpSocket to establish a socket to communicate with the server
2) Send a connection request to the server
3) Get the chat message entered by the user and send it to the server
4) Receive the message forwarded by the server in real time and display it
4 Code implementation

#include "serverwidget.h"
#include "ui_serverwidget.h"

ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ServerWidget),port(8000)
{
    ui->setupUi(this);
    tcpServer = new QTcpServer(this);
    timer = new QTimer(this);
    connect(tcpServer,&QTcpServer::newConnection,this,&ServerWidget::onNewConnection);
    //Regularly check if any client is disconnected, if the client is disconnected, remove it
    connect(timer,&QTimer::timeout,[=](){
        for(int i = 0;i < tcpClientList.size();i++){
            //If the socket communicating with the client is in the disconnected state
            if(tcpClientList.at(i)->state() == QAbstractSocket::UnconnectedState){
                    qDebug() << "ip address is:" << tcpClientList.at(i)->localAddress()<< "left the chat room";
                    tcpClientList.removeAt(i);
                    --i;
            }
        }
    });
}

ServerWidget::~ServerWidget()
{
    delete ui;
}

void ServerWidget::on_createBtn_clicked()
{
    //get port
    port = ui->portLineEdit->text().toShort();
    //Set listening server and port QHostAddress::Any:"0.0.0.0"
    if(tcpServer->listen(QHostAddress::Any,port))
        qDebug() << "Server created successfully";
    else{
           qDebug() << "Server creation failed";
           return;
    }
    timer->start(3000);
    ui->portLineEdit->setEnabled(false);
    ui->createBtn->setEnabled(false);
}
//Slot function for client connection request
void ServerWidget::onNewConnection(){
   //Get the socket to communicate with the client
  QTcpSocket* clientSocket = tcpServer->nextPendingConnection();
  //save socket to container
  tcpClientList.append(clientSocket);
  //When the client sends a message to the server, the communicating socket emits the readyRead signal
  connect(clientSocket,&QTcpSocket::readyRead,this,&ServerWidget::onReadyRead);
}
//Slot function for receiving client messages
void ServerWidget::onReadyRead(){
    //Traverse the container to check which socket in the socket has a message coming
    for(int i = 0;i < tcpClientList.size();i++){
        if(tcpClientList.at(i)->bytesAvailable()){
            //Read messages from the client
            QByteArray buf = tcpClientList.at(i)->readAll();
            //Show received messages
            ui->listWidget->addItem(buf);
            ui->listWidget->scrollToBottom();
            //forward message
            sendMessage(buf);
        }
    }
}
//Function to forward client messages
void ServerWidget::sendMessage(const QByteArray& msg){
    for(int i = 0;i<tcpClientList.size();i++){
        tcpClientList.at(i)->write(msg);
    }
}

Client:

#include "clientwidget.h"
#include "ui_clientwidget.h"

ClientWidget::ClientWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ClientWidget),status(false)
{
    ui->setupUi(this);

    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket,&QTcpSocket::connected,this,&ClientWidget::onConnected);
    connect(tcpSocket,&QTcpSocket::disconnected,this,&ClientWidget::onDisconnected);
    connect(tcpSocket,&QTcpSocket::readyRead,this,&ClientWidget::onReadyRead);
    connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError()));
}

ClientWidget::~ClientWidget()
{
    delete ui;
}

//connect to the server
void ClientWidget::on_entryBtn_clicked()
{
   //Connect offline
    if(status == false){
        QString ip = ui->IPLineEdit->text();
        if(!serverIp.setAddress(ip))
        {
            QMessageBox::critical(this,"Error","IP wrong address!");
            return;
        }
        serverPort = ui->PortLineEdit->text().toShort();
        if(serverPort < 1024){
            QMessageBox::critical(this,"error","wrong port number");
            return;
        }
        usrName = ui->UsrNameLineEdit->text();
        if(usrName == ""){
            QMessageBox::critical(this,"Error","Username can not be empty");
            return;
        }
        //connect to the server
        //success: connected
        //failed: error
        tcpSocket->connectToHost(serverIp,serverPort);

    }
    //online disconnect
    else{
            //Disconnect from server
            QString msg = ui->UsrNameLineEdit->text() + ":leave the chat room";
            tcpSocket->write(msg.toUtf8());
            tcpSocket->disconnectFromHost();
    }
}
//Send a message
void ClientWidget::on_sendBtn_clicked(){
    QString msg = ui->msgLineEdit->text();
    if(msg =="") return;
    QString info = usrName + ":" +msg;
    tcpSocket->write(info.toUtf8());
    ui->msgLineEdit->clear();
}
//disconnect from server
void ClientWidget::onDisconnected(){
    ui->sendBtn->setEnabled(false);
    ui->IPLineEdit->setEnabled(true);
    ui->PortLineEdit->setEnabled(true);
    ui->UsrNameLineEdit->setEnabled(true);
    ui->entryBtn->setText("enter the chat room");
    status  = false;
}
//The server has data coming
void ClientWidget::onReadyRead(){
    if(tcpSocket->bytesAvailable()){
        QByteArray buf =  tcpSocket->readAll();
        //show message
        ui->listWidget->addItem(buf);
        ui->listWidget->scrollToBottom();
    }
}
//network anomaly
void ClientWidget::onError(){
    QMessageBox::critical(this,"Error",tcpSocket->errorString());
}
void ClientWidget::onConnected(){
    status = true;
    ui->sendBtn->setEnabled(true);
    ui->IPLineEdit->setEnabled(false);
    ui->PortLineEdit->setEnabled(false);
    ui->UsrNameLineEdit->setEnabled(false);
    ui->entryBtn->setText("leave the chat room");
    QString msg = ui->UsrNameLineEdit->text();
    QString info = msg + ":enter the chat room";
    //Convert to utf-8
   tcpSocket->write(info.toUtf8());
}

Posted by Jorge on Sat, 14 May 2022 03:29:51 +0300