Qt中的TCP数据传输方式研究

一、TCP工作原理

Tcp是面向连接的一种流式通信,能够提供可靠的通信连接,使一台客户机发出的字节流无差错地送达网络上的其他计算器。如果是对可靠性要求高的数据通信系统则需要使用Tcp传输数据,但是要发送数据,则必须首先建立连接。

二、TCP编程模型

1)首先启动服务器,成功后启动客户端;

2)客户端与服务器进行三次握手后建立连接;

3)客户端向服务端发送请求;

4)服务端发送给客户端响应;

5)多次循环,知道通讯结束,最后关闭网络连接。

三、TCP通信传输类型:

1)基于行的方式

     基于行的数据通信协议用于一般用于纯文本数据的通信,每一行数据以换行符结束。

2)基于数据块方式

     基于块的数据通信协议用于一般的二进制数据传输,需要自定义具体格式。

四、代码示例:

1、效果图

2、代码示例

1)TCP服务端

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("监听状态:"));
    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()
{
//一、基于行的网络传输
//    while(tcpSocket->canReadLine())
//    
//        ui->plainTextEdit->appendPlainText("[client] " + tcpSocket->readLine());
//    }
    //二、基于块的网络传输
    //1)方案一:
//    while(tcpSocket->bytesAvailable() > 0)
//    {
//        ui->plainTextEdit->appendPlainText("[client] " + tcpSocket->readAll());
//    }
    //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("服务器监听失败!"));
    }
    else
    {
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("服务器监听成功!"));
        ui->connectBtn->setEnabled(false);
        ui->disconnectBtn->setEnabled(true);
        labListen->setText(QString::fromLocal8Bit("监听状态,正在监听"));
    }
}

void MainWindow::on_disconnectBtn_clicked()
{
    if(tcpServer->isListening())
    {
        tcpServer->close();//停止监听
        ui->connectBtn->setEnabled(true);
        ui->disconnectBtn->setEnabled(false);
        labListen->setText(QString::fromLocal8Bit("监听状态:已停止监听"));
    }
}

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)客户端

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("**已断开与服务器连接"));
    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()
{
  //一、基于行的网络传输
//    while(tcpClient->canReadLine())
//    {
//       ui->plainTextEdit->appendPlainText("[Server] " + tcpClient->readLine());
//    }
    //二、基于块的网络传输
    //1)方案一:
//    while(tcpClient->bytesAvailable() > 0)
//    {
//        ui->plainTextEdit->appendPlainText("[Server] " + tcpClient->readAll());
//    }
 //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();
}

总结:Qt中tcp传输与接收方式各种各样,具体怎么使用还需要根据场景来看,当然了除了以上三种方式外还有一种是Qt4中提到的序列化数据包形式,如:

//发送
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);
一开始写入0值作为块的大小,以及其他数据。然后,对输入/输出设备,调用seek(0)以重新
移动到字节数组的开始处,同时利用数据块的实际尺寸值覆盖最初写入的0值。这个尺寸值是
通过由数据块的尺寸减去sizeof(quint16)计算得到的,也就是去掉前面容量字段所占空间,
最后发出这个块。

//接收(这里设置了缓存块quin16 nextBlockSize)
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;

此种方式使用特别注意数据缓存块nextBlockSize这个得使用,其次要理解占位的概念,以及内存数据的计算数据移动等。

猜你喜欢

转载自blog.csdn.net/leiyang2014/article/details/124538623