一、TCP工作原理

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

以流的形式接收response_网络

二、TCP编程模型

以流的形式接收response_网络_02

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

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

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

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

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

三、TCP通信传输类型:

1)基于行的方式

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

2)基于数据块方式

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

四、代码示例:

1、效果图

以流的形式接收response_#include_03

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_OBJECTpublic:
     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_OBJECTpublic:
     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这个得使用,其次要理解占位的概念,以及内存数据的计算数据移动等。