TCP communication based on QT

1. Introduction

TCP communication must first establish a TCP link, and the communication end is divided into a client and a server. QT provides QTcpServer class and QTcpSocket class for establishing TCP communication applications. QTcpServer is used for port monitoring and establishing a server; QTcpSocket is used for communication using sockets (Socket) after establishing a connection.

The server-side program first needs to use QTcpServer::listen() to start server-side monitoring, and the IP address and port to monitor can be specified. Generally, a service program only monitors the network connection of a certain port.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (Qt actual combat project, C++ language foundation, C++ design mode, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

When a new client is connected, the incomingConnection() function inside QTcpServer will create a QTcpSocket object connected to the client, and then emit the signal newConnection(). In the slot function of the newConnection() signal, you can use nextPendingConnection() to accept the client's connection, and then use QTcpSocket to communicate with the client.

Therefore, after the client and server establish a TCP connection, the specific data communication is completed through QTcpSocket.

The QTcpSocket instance of the client first tries to connect to the server through connectToHost(), and the IP address and port of the server need to be specified. connectToHost() is an asynchronous way to connect to the server without blocking the running of the program, and emits the connected() signal after the connection.

After establishing a socket link with the server, you can write data to the buffer or read data from the receiving buffer to realize data communication. When new data enters the buffer, the readyRead() signal is emitted, and the buffer data is generally read in the slot function of this signal.

QTcpSocket is indirectly inherited from QIODevice, so you can use the streaming data reading and writing function. A QTcpSocket instance can both receive data and send data, and receiving and sending work asynchronously, with their own buffers.

Two, TCPServer (TCP server design)

The TCPServer program has the following functions:

Open network monitoring according to the specified IP address (local address) and port, and create a socket link when there is a client connection

Using the line-based data communication protocol, it can receive messages from the client and send messages to the client

Display server listening status and socket status in the status bar

The ui interface is as follows:

programming

TCPServer is a window-based application program of QMainWindow, the program name is QTCPServer. Where mainwindow.h is as follows:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QTcpServer>
#include <QHostInfo>
#include <QTcpSocket>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QLabel *LabListen;
    QLabel *LabSocketState;
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;
    QStringList getLocalIP();
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    void onNewConnection();         //QTcpServer的newConnection()信号
    void onSocketStateChange(QAbstractSocket::SocketState);
    void onClientConnected();       //Client Socket connected
    void onClientDisconnected();    //Client Socket disconnected
    void onSocketReadRead();        //读取socket传入的数据
    void on_act_start_triggered();
    void on_act_stop_triggered();
    void on_pb_sendmessage_clicked();
};

#endif // MAINWINDOW_H

mainwindow.cpp is as follows:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    LabListen = new QLabel("监听状态:");
    LabListen->setMinimumWidth(200);
    ui->statusBar->addWidget(LabListen);

    LabSocketState = new QLabel("Socket 状态:");
    LabSocketState->setMinimumWidth(250);
    ui->statusBar->addWidget(LabSocketState);

    QStringList localeIP = getLocalIP(); //本机IP
    ui->cb_address->addItems(localeIP);
    tcpServer = new QTcpServer(this);
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));
}

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

QStringList MainWindow::getLocalIP()
{
    //获取本机IPV4地址
    QString hostName = QHostInfo::localHostName();      //本地主机名
    QHostInfo hostInfo = QHostInfo::fromName(hostName); //根据主机名获取ip
    QStringList localIp;
    QList<QHostAddress> addList = hostInfo.addresses();

    if(!addList.isEmpty())
    {
        for(int i = 0;i < addList.count();i++)
        {
            QHostAddress aAddress = addList.at(i);
            if(QAbstractSocket::IPv4Protocol == aAddress.protocol())
            {
                localIp.append(aAddress.toString());
            }
        }
    }
    return localIp;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event);
}

void MainWindow::onNewConnection()
{
    tcpSocket = tcpServer->nextPendingConnection();  //获取socket
    connect(tcpSocket,SIGNAL(connected()),this,SLOT(onClientConnected()));  //connected()信号,客户端socket连接时发射此信号
    onClientConnected();
    connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(onClientDisconnected()));    //disconnected()信号,客户端socket断开时发射此信号
    connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadRead()));
}

void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketstate)
{
    //socket状态变化
    switch (socketstate)
    {
    case QAbstractSocket::UnconnectedState:
        LabSocketState->setText("socket状态:UnconnectedState");
        break;
    case QAbstractSocket::HostLookupState:
        LabSocketState->setText("socket状态:HostLookupState");
        break;
    case QAbstractSocket::ConnectingState:
        LabSocketState->setText("socket状态:ConnectingState");
        break;
    case QAbstractSocket::ConnectedState:
        LabSocketState->setText("socket状态:ConnectedState");
        break;
    case QAbstractSocket::BoundState:
        LabSocketState->setText("socket状态:BoundState");
        break;
    case QAbstractSocket::ClosingState:
        LabSocketState->setText("socket状态:ClosingState");
        break;
    case QAbstractSocket::ListeningState:
        LabSocketState->setText("socket状态:ListeningState");
        break;
    }
}

void MainWindow::onClientConnected()
{
    //客户端接入时
    ui->plainTextEdit->appendPlainText("**client socket connected");
    ui->plainTextEdit->appendPlainText("**peer address:"+tcpSocket->peerAddress().toString());
    ui->plainTextEdit->appendPlainText("**peer port:"+tcpSocket->peerPort());
}

void MainWindow::onClientDisconnected()
{   //客户端断开的时候
    ui->plainTextEdit->appendPlainText("**client socket disconnected");
    tcpSocket->deleteLater(); //deleteLater 会在当前对象的所有事件处理完成后再删除对象
}

void MainWindow::onSocketReadRead()
{
    //读取缓冲区行文本
    while (tcpSocket->canReadLine()) {
       ui->plainTextEdit->appendPlainText("[In] "+tcpSocket->readLine());
    }
}

void MainWindow::on_act_start_triggered()
{
    //开始监听
    QString IP = ui->cb_address->currentText(); //获取IP
    quint16 port = ui->sb_port->value();        //获取端口
//    QHostAddress addr(IP);                      //
    tcpServer->listen(QHostAddress::LocalHost,port);               //开始监听
    ui->plainTextEdit->appendPlainText("**开始监听...");
    ui->plainTextEdit->appendPlainText("**服务器地址:"+tcpServer->serverAddress().toString());
    ui->plainTextEdit->appendPlainText("**服务器端口:"+QString::number(tcpServer->serverPort()));
    ui->act_start->setEnabled(false);
    ui->act_stop->setEnabled(true);
    LabListen->setText("监听状态:正在监听");
}

void MainWindow::on_act_stop_triggered()
{
    //停止监听
    if(tcpServer->isListening()) //tcpServer正在监听
    {
        tcpServer->close();
        ui->act_start->setEnabled(true);
        ui->act_stop->setEnabled(false);
    }
    LabListen->setText("监听状态:已经停止监听");
}

void MainWindow::on_pb_sendmessage_clicked()
{
    //发送一行字符串,以换行符结束
    QString msg = ui->le_message->text();
    ui->plainTextEdit->appendPlainText("[Out] "+msg);
    ui->le_message->clear();
    ui->le_message->setFocus();

    QByteArray str = msg.toUtf8();
    str.append("\n");   //添加一个换行符
    tcpSocket->write(str);
}

As a TCP server, the QTcpServer class needs to call listen() to start TCP monitoring on a certain IP address and port of the machine, so as to wait for the access of the TCP client. Click the "Start Monitoring" button on the main window to start network monitoring.

After tcpServer starts listening, TCPClient can connect to this server through IP address and port. When a client connects, tcpServer emits a newConnection() signal.

The program first obtains the QTcpSocket object instance tcpSocket that communicates with the connection through the nextPendingConnection() function, and then links several signals of tcpSocket with the corresponding slot functions. The functions of these signals of QTcpSocket are:

connected() signal, which is emitted when the client socket connection is established

disconnected() signal, which is emitted when the client socket connection is disconnected

stateChanged(), this signal is emitted when the socket state of this program changes

readyRead(), this signal is emitted when there is new data in the read buffer of the socket of this program

The TCP server stops listening, just call the close() function of QTcpServer.

Three, TCPClient (TCP client design)

The TCPClient program has the following functions:

  • Connect to server by IP address and port number
  • Using the line-based data communication protocol to send and receive messages with the server
  • Process the StateChange() signal of QTcpSocket, and display the status of the socket in the status bar

The ui interface is as follows:

programming

TCPClient is also a window-based QMainWindow application, where the main window defines mainwindow.h as follows:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostInfo>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QLabel *LabSocketState;
    QTcpSocket *tcpClient;  //socket
    QStringList getLocalIP();
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    void onCennected();
    void onDiscennected();
    void onSocketStateChange(QAbstractSocket::SocketState socketState);
    void onSocketReadyRead();   //读取socket传入的数据
    void on_act_connect_triggered();
    void on_act_disconnect_triggered();
    void on_pb_send_clicked();
};

#endif // MAINWINDOW_H

mainwindow.cpp is as follows:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    tcpClient = new QTcpSocket(this);   //创建socket变量
    LabSocketState = new QLabel("socket状态:"); //状态栏标签
    LabSocketState->setMinimumWidth(250);

    ui->statusBar->addWidget(LabSocketState);
    QStringList localIp = getLocalIP();
    ui->cb_address->addItems(localIp);

    connect(tcpClient,SIGNAL(connected()),this,SLOT(onCennected()));
    connect(tcpClient,SIGNAL(disconnected()),this,SLOT(onDiscennected()));
    connect(tcpClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    connect(tcpClient,SIGNAL(readyRead()),SLOT(onSocketReadyRead()));
}

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

QStringList MainWindow::getLocalIP()
{
    QString localeName = QHostInfo::localHostName();
    QHostInfo ipInfo = QHostInfo::fromName(localeName);
    QStringList ipList;
    QList<QHostAddress> addList = ipInfo.addresses();
    if(!addList.isEmpty())
    {
        for(int i = 0; i < addList.count(); i++)
        {
            QHostAddress aHost = addList.at(i);
            if(QAbstractSocket::IPv4Protocol == aHost.protocol())
            {
                ipList.append(aHost.toString());
            }
        }
    }
    return ipList;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event);
}

void MainWindow::onCennected()
{
    //connected()信号槽函数
    ui->plainTextEdit->appendPlainText("**已连接到服务器");
    ui->plainTextEdit->appendPlainText("**peer address:"+tcpClient->peerAddress().toString());
    ui->plainTextEdit->appendPlainText("**peer port:"+QString::number(tcpClient->peerPort()));
    ui->act_connect->setEnabled(false);
    ui->act_disconnect->setEnabled(true);
}

void MainWindow::onDiscennected()
{
    //disconnected()信号槽函数
    ui->plainTextEdit->appendPlainText("**已断开与服务器的连接");
    ui->act_connect->setEnabled(true);
    ui->act_disconnect->setEnabled(false);
}

void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    //socket状态变化
    switch (socketState)
    {
    case QAbstractSocket::UnconnectedState:
        LabSocketState->setText("socket状态:UnconnectedState");
        break;
    case QAbstractSocket::HostLookupState:
        LabSocketState->setText("socket状态:HostLookupState");
        break;
    case QAbstractSocket::ConnectingState:
        LabSocketState->setText("socket状态:ConnectingState");
        break;
    case QAbstractSocket::ConnectedState:
        LabSocketState->setText("socket状态:ConnectedState");
        break;
    case QAbstractSocket::BoundState:
        LabSocketState->setText("socket状态:BoundState");
        break;
    case QAbstractSocket::ClosingState:
        LabSocketState->setText("socket状态:ClosingState");
        break;
    case QAbstractSocket::ListeningState:
        LabSocketState->setText("socket状态:ListeningState");
        break;
    }
}

void MainWindow::onSocketReadyRead()
{
    //readyRead()信号槽函数
    while(tcpClient->canReadLine())
    {
        ui->plainTextEdit->appendPlainText("[in] "+tcpClient->readLine());
    }
}

void MainWindow::on_act_connect_triggered()
{
    //"连接到服务器"按钮
    QString addr = ui->cb_address->currentText();
    qint16 port = ui->sb_port->value();
    tcpClient->connectToHost(addr,port);
}

void MainWindow::on_act_disconnect_triggered()
{
    if(tcpClient->state() == QAbstractSocket::ConnectedState)
    {
        tcpClient->disconnectFromHost();
    }
}

void MainWindow::on_pb_send_clicked()
{
    QString msg = ui->le_message->text();
    ui->plainTextEdit->appendPlainText("[out] "+msg);
    ui->le_message->clear();
    ui->le_message->setFocus();
    QByteArray str = msg.toUtf8();
    str.append("\n");
    tcpClient->write(str);
}

After setting the server IP address on the window, call the QTcpSocket function connectToHost() to connect to the server, or use the disconnectFromHost() function to disconnect from the server.

Among them, the function and code of the slot function onSocketStateChange() are exactly the same as those of TcpServer.

The line-based data communication protocol is adopted between TCPClient and TCPServer. Clicking the "Send Message" button will send a line of string. Read the string in the slot function of the readyRead() signal.

The examples of TCPServer and TCPClient simply demonstrate the principle of TCP communication, and TCPServer only allows one TCPClient client to access. The general TCP server program allows multiple clients to access. In order to make each socket link communicate independently and not affect each other, multi-threading is generally used, that is, a new thread is created for a socket link.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (Qt actual combat project, C++ language foundation, C++ design mode, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Guess you like

Origin blog.csdn.net/m0_73443478/article/details/132601960