Research on TCP data transmission mode in Qt

1. Working principle of TCP

Tcp is a connection-oriented streaming communication that can provide a reliable communication connection, so that the byte stream sent by a client computer can be delivered to other computers on the network without error. If it is a data communication system that requires high reliability, it needs to use Tcp to transmit data, but to send data, a connection must be established first.

Two, TCP programming model

1) Start the server first, and start the client after success;

2) The client and the server perform a three-way handshake to establish a connection;

3) The client sends a request to the server;

4) The server sends a response to the client;

5) Repeat multiple times until the communication ends, and finally close the network connection.

3. TCP communication transmission type:

1) Row-based approach

     The line-based data communication protocol is used for the communication of plain text data in general, and each line of data is terminated by a newline character.

2) Based on the data block method

     The block-based data communication protocol is used for general binary data transmission and needs to customize the specific format.

4. Code example:

1. Rendering

2. Code example

1) TCP server

mainwindow.h :

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

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

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void onNewConnection();
    void onSocketStateChange(QAbstractSocket::SocketState);
    void onClientConnected();
    void onClientDisconnected();
    void onSocketReadyRead();
    void on_connectBtn_clicked();
    void on_disconnectBtn_clicked();
    void on_sendBtn_clicked();
    void on_clearBtn_clicked();

private:
    Ui::MainWindow *ui;
    QLabel  *labListen;
    QLabel  *labSocketState;
    QTcpServer  *tcpServer;
    QTcpSocket  *tcpSocket;
};

#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHostInfo>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QString localIP = getLocalIP();
    ui->ipLineEdit->setText(localIP);
    ui->portLineEdit->setText("1000");

    labListen = new QLabel(QString::fromLocal8Bit("Listening status:"));
    labListen->setMinimumWidth(150);
    ui->statusBar->addWidget(labListen);

    labSocketState = new QLabel(QString::fromLocal8Bit("Socket状态:"));
    labSocketState->setMinimumWidth(200);
    ui->statusBar->addWidget(labSocketState);

    tcpServer = new QTcpServer(this);
    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}

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

QString MainWindow::getLocalIP()
{
    QString hostName = QHostInfo::localHostName(); //主机名
    QHostInfo hostInfo = QHostInfo::fromName(hostName);
    QString localIP = "";
    QList<QHostAddress> addList = hostInfo.addresses();

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

void MainWindow::onNewConnection()
{
    tcpSocket = tcpServer->nextPendingConnection();
    connect(tcpSocket, SIGNAL(connected()), this, SLOT(onClientConnected()));
    onClientConnected();
    connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
    connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    onSocketStateChange(tcpSocket->state());
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}

void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    switch (socketState)
    {
    case QAbstractSocket::UnconnectedState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: UnconnectedState"));
        break;
    case QAbstractSocket::HostLookupState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: HostLookupState"));
        break;
    case QAbstractSocket::ConnectingState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ConnectingState"));
        break;
    case QAbstractSocket::ConnectedState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ConnectedState"));
        break;
    case QAbstractSocket::BoundState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: BoundState"));
        break;
    case QAbstractSocket::ClosingState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ClosingState"));
        break;
    case QAbstractSocket::ListeningState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ListeningState"));
        break;
    default:
        break;
    }
}

void MainWindow::onClientConnected()
{
    ui->plainTextEdit->appendPlainText("**client socket connected");
    ui->plainTextEdit->appendPlainText("**peer address:" + tcpSocket->peerAddress().toString());
    ui->plainTextEdit->appendPlainText("**peer port:" + QString::number(tcpServer->serverPort()));
    ui->connectBtn->setEnabled(false);
    ui->disconnectBtn->setEnabled(true);
}

void MainWindow::onClientDisconnected()
{
     ui->plainTextEdit->appendPlainText("**client socket disconnected");
     tcpSocket->deleteLater();
}

void MainWindow::onSocketReadyRead()
{ //1. Line-based network transmission // while(tcpSocket->canReadLine()) //     // ui->plainTextEdit->appendPlainText("[client] " + tcpSocket->readLine ()); // }     //2. Block-based network transmission     //1) Solution 1: // while(tcpSocket->bytesAvailable() > 0) // { // ui->plainTextEdit->appendPlainText(" [client] " + tcpSocket->readAll()); // }     //2) Solution 2:   // while (!tcpSocket->atEnd())     //{     // QByteArray data = tcpSocket->read(255) ;     // ui->plainTextEdit->appendPlainText("[client] " + data);     //}
















 //3)方案三
 while(tcpSocket->bytesAvailable() > 0)
 {
   int length = tcpSocket->bytesAvailable();
   char buf[1024];
   tcpSocket->read(buf, length);
   QString msg = buf;
   ui->plainTextEdit->appendPlainText("[client] " + msg);
 }
}

void MainWindow::on_connectBtn_clicked()
{
    QString IP = ui->ipLineEdit->text();
    quint16 port = ui->portLineEdit->text().toUInt();

    if(!tcpServer->listen(QHostAddress(IP), port))
    {         ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("Server listening failed!"));     }     else     {         ui->plainTextEdit->appendPlainText( QString::fromLocal8Bit("The server is listening successfully!"));         ui->connectBtn->setEnabled(false);         ui->disconnectBtn->setEnabled(true);         labListen->setText(QString::fromLocal8Bit("Listening status, Listening "));     } }









void MainWindow::on_disconnectBtn_clicked()
{     if(tcpServer->isListening())     {         tcpServer->close();//stop listening         ui->connectBtn->setEnabled(true);         ui->disconnectBtn->setEnabled(false) ;         labListen->setText(QString::fromLocal8Bit("Listening status: stop listening"));     } }







void MainWindow::on_sendBtn_clicked()
{
    QString msg = ui->MsgLineEdit->text();
    ui->plainTextEdit->appendPlainText("[server] " + msg);
    ui->MsgLineEdit->clear();
    ui->MsgLineEdit->setFocus();

//    QByteArray dataArray = msg.toUtf8();
//    dataArray.append('\n');
//    tcpSocket->write(dataArray);
    QByteArray dataArray = msg.toUtf8();
    tcpSocket->write(dataArray);
    tcpSocket->flush();
}

void MainWindow::on_clearBtn_clicked()
{
    ui->MsgLineEdit->clear();
    ui->plainTextEdit->clear();
}

2) client

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QLabel>
#include <QAbstractSocket>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    QString getLocalIP();
private slots:
    void onConnected();
    void onDisconnected();
    void onSocketStateChange(QAbstractSocket::SocketState);
    void onSocketReadyRead();
    void on_connectBtn_clicked();

    void on_disconnectBtn_clicked();

    void on_sendBtn_clicked();

    void on_clearBtn_clicked();

private:
    Ui::MainWindow *ui;
    QTcpSocket  *tcpClient;
    QLabel *labSocketState;

};

#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHostInfo>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle(QString::fromLocal8Bit("客户端"));

    QString localIP = getLocalIP();
    ui->ipLineEdit->setText(localIP);
    ui->portLineEdit->setText("1000");

    labSocketState = new QLabel(QString::fromLocal8Bit("Socket状态:"));
    labSocketState->setMinimumWidth(250);
    ui->statusBar->addWidget(labSocketState);
    //tcpSocket
    tcpClient = new QTcpSocket(this);

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

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

QString MainWindow::getLocalIP()
{
    QString hostName = QHostInfo::localHostName(); //主机名
    QHostInfo hostInfo = QHostInfo::fromName(hostName);
    QString localIP = "";
    QList<QHostAddress> addList = hostInfo.addresses();

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

void MainWindow::onConnected()
{
    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已连接到服务器"));
    ui->plainTextEdit->appendPlainText("**peer address:" + tcpClient->peerAddress().toString());
    ui->plainTextEdit->appendPlainText("**peer port:" + QString::number(tcpClient->peerPort()));
    ui->connectBtn->setEnabled(false);
    ui->disconnectBtn->setEnabled(true);
}

void MainWindow::onDisconnected()
{     ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**Disconnected from the server"));     ui->connectBtn->setEnabled(true);     ui->disconnectBtn- >setEnabled(false); }



void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    switch (socketState)
    {
    case QAbstractSocket::UnconnectedState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: UnconnectedState"));
        break;
    case QAbstractSocket::HostLookupState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: HostLookupState"));
        break;
    case QAbstractSocket::ConnectingState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ConnectingState"));
        break;
    case QAbstractSocket::ConnectedState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ConnectedState"));
        break;
    case QAbstractSocket::BoundState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: BoundState"));
        break;
    case QAbstractSocket::ClosingState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ClosingState"));
        break;
    case QAbstractSocket::ListeningState:
        labSocketState->setText(QString::fromLocal8Bit("socket 状态: ListeningState"));
        break;
    default:
        break;
    }
}

void MainWindow::onSocketReadyRead()
{   //1. Line-based network transmission // while(tcpClient->canReadLine()) // { // ui->plainTextEdit->appendPlainText("[Server] " + tcpClient-> readLine()); // }     //2. Block-based network transmission     //1) Solution 1: // while(tcpClient->bytesAvailable() > 0) // { // ui->plainTextEdit->appendPlainText( "[Server] " + tcpClient->readAll()); // }  //2) Solution 2: //while (!tcpClient->atEnd()) //{ // QByteArray data = tcpClient->read(255 ); // ui->plainTextEdit->appendPlainText("[Server] " + data); //}
















//3)方案三
 while(tcpClient->bytesAvailable() > 0)
 {
    QByteArray datagram;
    datagram.resize(tcpClient->bytesAvailable());
    tcpClient->read(datagram.data(), datagram.size());
    QString msg = datagram.data();
    ui->plainTextEdit->appendPlainText("[Server] " + msg);
  }
}

void MainWindow::on_connectBtn_clicked()
{
    QString addrIP = ui->ipLineEdit->text();
    quint16 port = ui->portLineEdit->text().toUInt();
    tcpClient->connectToHost(addrIP, port);
}

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

void MainWindow::on_sendBtn_clicked()
{
    QString msg = ui->MsgLineEdit->text();
    ui->plainTextEdit->appendPlainText("[Client] " + msg);
    ui->MsgLineEdit->clear();
    ui->MsgLineEdit->setFocus();

//    QByteArray dataArray = msg.toUtf8();
//    dataArray.append('\n');
//    tcpClient->write(dataArray);

    QByteArray dataArray = msg.toUtf8();
    tcpClient->write(dataArray);
    tcpClient->flush();
}

void MainWindow::on_clearBtn_clicked()
{
    ui->MsgLineEdit->clear();
    ui->plainTextEdit->clear();
}

Summary: There are various ways of tcp transmission and reception in Qt. How to use it depends on the scenario. Of course, in addition to the above three ways, there is also a serialized data packet form mentioned in Qt4, such as:

//Send
QByteArray dataArray; 
QDataStream out(&dataArray, QIODevice::WriteOnly);
out.setVersion(QDatastream::Qt_4_7);
out<<quin16(0)<<quint8('S')<<currentData; 
out.device( )->seek(0);
out<<quint16(dataArray.size()- sizeof(quin16));
tcpSocket.write(dataArray);
Initially write 0 value as the block size, and other data. Then, for the I/O device, call seek(0) to re-
move to the beginning of the byte array, while overwriting the originally written 0 value with the actual size of the data block. This size value is
calculated by subtracting sizeof(quint16) from the size of the data block, that is, the space occupied by the previous capacity field is removed, and
the block is finally issued.

//Receive (the cache block quin16 nextBlockSize is set here)
QDatastream in(&tcpSocket);
in.setVersion(QDatastream::Qt_4_7);

if(nextBlockSize == 0)
{
    if(bytesAvailable() < sizeof(quint16))
        return;
    in >> nextBlockSize;
}
if(bytesAvailable() < nextBlockSize)
    return;

quint8 requestType;
QString sData;

in >> requestType >> sData;

When using this method, pay special attention to the use of the data cache block nextBlockSize. Secondly, you must understand the concept of space occupation, and the calculation and data movement of memory data.

Guess you like

Origin blog.csdn.net/leiyang2014/article/details/124538623