天天看點

QT學習筆記(12) QT下的TCP通信

一、TCP通信過程

(1)伺服器端:

  伺服器端有QTcpServer的監聽套接字,運用listen()方法監聽網卡的ip和端口。

  如果有新的連接配接傳過來,并且連接配接成功,伺服器會觸發newConnection(),通過槽函數取出連接配接過來的通信套接字QTcpSocket

  如果有資料成功傳送過來,對方的通信套接字QTcpSocket會觸發readyRead(),通過槽函數可以對接收的資料進行處理

(2)用戶端

  首先根據ip和端口,通過通信套接字QTcpSocket的connectToHost()方法主動和伺服器建立連接配接

  如果連接配接成功,通信套接字QTcpSocket會自動觸發connected(),通過槽函數可以進行操作

  如果有資料成功傳送過來,對方的通信套接字QTcpSocket會觸發readyRead(),通過槽函數可以對接收的資料進行處理

    

QT學習筆記(12) QT下的TCP通信

二、執行個體代碼如下:

QT_HelloWorld11.pro

1 #-------------------------------------------------
 2 #
 3 # Project created by QtCreator 2017-08-30T21:18:55
 4 #
 5 #-------------------------------------------------
 6 
 7 QT       += core gui network #添加network子產品
 8 
 9 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
10 
11 TARGET = QT_HelloWorld11
12 TEMPLATE = app
13 
14 
15 SOURCES += main.cpp\
16         serverwidget.cpp \
17     clientwidget.cpp
18 
19 HEADERS  += serverwidget.h \
20     clientwidget.h
21 
22 FORMS    += serverwidget.ui \
23     clientwidget.ui
24 
25 CINFIG += C++11      

main.cpp

1 #include "serverwidget.h"
 2 #include <QApplication>
 3 //包含頭檔案
 4 #include "clientwidget.h"
 5 
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     QApplication a(argc, argv);
10     //顯示伺服器視窗
11     ServerWidget w;
12     w.show();
13 
14     //顯示用戶端視窗
15     ClientWidget w2;
16     w2.show();
17 
18     return a.exec();
19 }      

serverwidget.h

1 #ifndef SERVERWIDGET_H
 2 #define SERVERWIDGET_H
 3 
 4 #include <QWidget>
 5 #include <QTcpServer>//監聽套接字
 6 #include <QTcpSocket>//通信套接字(建立好連接配接的套接字)
 7 
 8 namespace Ui {
 9 class ServerWidget;
10 }
11 
12 class ServerWidget : public QWidget
13 {
14     Q_OBJECT
15 
16 public:
17     explicit ServerWidget(QWidget *parent = 0);
18     ~ServerWidget();
19 
20 private slots:
21     void on_button_send_clicked();
22 
23     void on_button_close_clicked();
24 
25 private:
26     Ui::ServerWidget *ui;
27 
28     QTcpServer * tcpServer;//監聽套接字
29     QTcpSocket * tcpSocket;//通信套接字
30 
31 };
32 
33 #endif // SERVERWIDGET_H      

clientwidget.h

1 #ifndef CLIENTWIDGET_H
 2 #define CLIENTWIDGET_H
 3 
 4 #include <QWidget>
 5 #include <QTcpSocket>//通信套接字
 6 
 7 namespace Ui {
 8 class ClientWidget;
 9 }
10 
11 class ClientWidget : public QWidget
12 {
13     Q_OBJECT
14 
15 public:
16     explicit ClientWidget(QWidget *parent = 0);
17     ~ClientWidget();
18 
19 private slots:
20     void on_pushButton_connect_clicked();
21 
22     void on_pushButton_send_clicked();
23 
24     void on_pushButton_close_clicked();
25 
26 private:
27     Ui::ClientWidget *ui;
28 
29     QTcpSocket *tcpSocket;
30 };
31 
32 #endif // CLIENTWIDGET_H      

serverwidget.cpp

1 #include "serverwidget.h"
 2 #include "ui_serverwidget.h"
 3 #include <QTcpServer>//監聽套接字
 4 #include <QTcpSocket>//通信套接字(建立好連接配接的套接字)
 5 
 6 ServerWidget::ServerWidget(QWidget *parent) :
 7     QWidget(parent),
 8     ui(new Ui::ServerWidget)
 9 {
10     ui->setupUi(this);
11 
12     setWindowTitle(QString::fromLocal8Bit("伺服器:8888"));
13 
14     tcpServer = NULL;//先指派為空,在後面做空值判斷,防止空指針的錯誤
15     tcpSocket = NULL;
16 
17     //執行個體化 監聽套接字
18     tcpServer = new QTcpServer(this);//指定父對象,讓其自動回收空間
19     //監聽
20     tcpServer->listen(QHostAddress::Any,8888);//預設綁定目前網卡上的所有ip
21 
22     //如果有新的連接配接傳過來,并連接配接成功,伺服器觸發newConnection()方法
23     connect(tcpServer,&QTcpServer::newConnection,
24             [=]()
25             {
26                 //取出建立好連接配接的套接字
27                 tcpSocket = tcpServer->nextPendingConnection();//取出目前最近的一次連接配接的套接字
28                 //擷取對方(用戶端)的IP和端口
29                 QString ip = tcpSocket->peerAddress().toString();
30                 qint16 port = tcpSocket->peerPort();
31                 QString temp = QString("[%1:%2]:成功連接配接").arg(ip).arg(port);//組包
32                 //在伺服器端顯示
33                 ui->textEdit_read->setText(temp);
34 
35                 //此處不能直接放在構造函數中,因為如果直接放在那,還沒配置設定tcpSocket空間,會程式異常
36                 //如果有資料傳送成功,對方的通信套接字會觸發readyRead(),需要在對應的槽函數做接收處理
37                 connect(tcpSocket,&QTcpSocket::readyRead,
38                         [=]()
39                         {
40                             //從通信套接字中取出内容
41                             QByteArray array = tcpSocket->readAll();
42                             //然後将内容追加到顯示文本中
43                             ui->textEdit_read->append(array);
44                         }
45                         );
46 
47                 //如果對方主動斷開連接配接,通信套接字會自動觸發disconnected()
48                 connect(tcpSocket,&QTcpSocket::disconnected,
49                         [=]()
50                         {
51                             ui->textEdit_read->append(QString::fromLocal8Bit("對方主動斷開連接配接"));
52                         }
53                         );
54             }
55             );
56 
57 }
58 
59 ServerWidget::~ServerWidget()
60 {
61     delete ui;
62 }
63 
64 void ServerWidget::on_button_send_clicked()
65 {
66     if(tcpSocket == NULL)
67     {
68         return;
69     }
70 
71     //擷取編輯區内容
72     QString str = ui->textEdit_write->toPlainText();
73     //給對方發送資料,使用套接字是tcpSocket
74     tcpSocket->write(str.toUtf8().data());
75 }
76 
77 void ServerWidget::on_button_close_clicked()
78 {
79     if(tcpSocket == NULL)
80     {
81         return;
82     }
83     //主動和用戶端斷開連接配接
84     tcpSocket->disconnectFromHost();
85     tcpSocket->close();
86 
87     tcpSocket = NULL;
88 }      

clientwidget.cpp

1 #include "clientwidget.h"
 2 #include "ui_clientwidget.h"
 3 //需要包含頭檔案
 4 #include <QHostAddress>
 5 
 6 ClientWidget::ClientWidget(QWidget *parent) :
 7     QWidget(parent),
 8     ui(new Ui::ClientWidget)
 9 {
10     ui->setupUi(this);
11     setWindowTitle(QString::fromLocal8Bit("用戶端"));
12 
13     tcpSocket = NULL;
14 
15     //配置設定空間,指定父對象
16     tcpSocket = new QTcpSocket(this);
17 
18     //當tcpSocket套接字建立連接配接成功
19     //如果成功和對方建立連接配接,通信套接字會自動觸發connected()
20     connect(tcpSocket,&QTcpSocket::connected,
21             [=]()
22             {
23                 ui->textEdit_read->setText(QString::fromLocal8Bit("成功和伺服器建立連接配接"));
24             }
25             );
26 
27     //如果有資料傳送成功,對方的通信套接字會觸發readyRead(),需要在對應的槽函數做接收處理
28     connect(tcpSocket,&QTcpSocket::readyRead,
29             [=]()
30             {
31                 //從通信套接字中取出内容
32                 QByteArray array = tcpSocket->readAll();
33                 //然後将内容追加到顯示文本中
34                 ui->textEdit_read->append(array);
35             }
36             );
37 
38     //如果對方主動斷開連接配接,通信套接字會自動觸發disconnected()
39     connect(tcpSocket,&QTcpSocket::disconnected,
40             [=]()
41             {
42                 ui->textEdit_read->append(QString::fromLocal8Bit("對方主動斷開連接配接"));
43             }
44             );
45 
46 }
47 
48 ClientWidget::~ClientWidget()
49 {
50     delete ui;
51 }
52 
53 void ClientWidget::on_pushButton_connect_clicked()
54 {
55     //擷取伺服器ip和端口
56     QString ip = ui->lineEdit_IP->text();
57     qint16 port = ui->lineEdit_port->text().toInt();
58 
59     //主動和伺服器建立連接配接
60     tcpSocket->connectToHost(QHostAddress(ip),port);
61 }
62 
63 void ClientWidget::on_pushButton_send_clicked()
64 {
65     //擷取編輯框内容
66     QString str = ui->textEdit_Write->toPlainText();
67     //發送資料
68     tcpSocket->write( str.toUtf8().data() );
69 }
70 
71 void ClientWidget::on_pushButton_close_clicked()
72 {
73     //主動和伺服器斷開連接配接
74     tcpSocket->disconnectFromHost();
75     tcpSocket->close();
76 }      

serverwidget.ui

QT學習筆記(12) QT下的TCP通信

clientwidget.ui

QT學習筆記(12) QT下的TCP通信

三、TCP傳遞檔案

  實作以下功能:

    用戶端連接配接到伺服器

    伺服器端選擇檔案,然後發送

    用戶端接收檔案,并提示接收成功

  大概流程圖如下:

    

QT學習筆記(12) QT下的TCP通信

代碼如下:

QT_HelloWorld14.pro

1 #-------------------------------------------------
 2 #
 3 # Project created by QtCreator 2017-08-31T19:08:18
 4 #
 5 #-------------------------------------------------
 6 
 7 QT       += core gui network
 8 
 9 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
10 
11 TARGET = QT_HelloWorld14
12 TEMPLATE = app
13 
14 
15 SOURCES += main.cpp\
16         serverwidget.cpp \
17     clientwidget.cpp
18 
19 HEADERS  += serverwidget.h \
20     clientwidget.h
21 
22 FORMS    += serverwidget.ui \
23     clientwidget.ui
24 
25 CONFIG += C++11      

main.cpp

1 #include "serverwidget.h"
 2 #include <QApplication>
 3 #include "clientwidget.h"
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     QApplication a(argc, argv);
 8     ServerWidget w;
 9     w.show();
10     //顯示用戶端視窗
11     ClientWidget w2;
12     w2.show();
13 
14     return a.exec();
15 }      

clientwidget.h

1 #ifndef CLIENTWIDGET_H
 2 #define CLIENTWIDGET_H
 3 
 4 #include <QWidget>
 5 #include <QTcpSocket>
 6 #include <QFile>
 7 
 8 namespace Ui {
 9 class ClientWidget;
10 }
11 
12 class ClientWidget : public QWidget
13 {
14     Q_OBJECT
15 
16 public:
17     explicit ClientWidget(QWidget *parent = 0);
18     ~ClientWidget();
19 
20 private slots:
21     void on_pushButton_connect_clicked();
22 
23 private:
24     Ui::ClientWidget *ui;
25     QTcpSocket *tcpSocket;//通信套接字
26 
27     QFile file;//檔案對象
28     QString fileName;//檔案名字
29     qint64 fileSize;//檔案大小
30 
31     qint64 receiveSize;//已經接收檔案的大小
32     bool isStart ;//标志位,判斷是不是開始的部分(檔案名和檔案大小)
33 };
34 
35 #endif // CLIENTWIDGET_H      

serverwidget.h

1 #ifndef SERVERWIDGET_H
 2 #define SERVERWIDGET_H
 3 
 4 #include <QWidget>
 5 #include <QTcpServer>//監聽套接字
 6 #include <QTcpSocket>//通信套接字
 7 #include <QFile>
 8 #include <QTimer>
 9 
10 
11 namespace Ui {
12 class ServerWidget;
13 }
14 
15 class ServerWidget : public QWidget
16 {
17     Q_OBJECT
18 
19 public:
20     explicit ServerWidget(QWidget *parent = 0);
21     ~ServerWidget();
22 
23 private slots:
24     void on_pushButton_file_clicked();
25 
26     void on_pushButton_send_clicked();
27 
28     void sendDate();//發送檔案資料
29 
30 private:
31     Ui::ServerWidget *ui;
32     QTcpServer *tcpServer;//監聽套接字
33     QTcpSocket *tcpSocket;//通信套接字
34 
35     QFile file;//檔案對象
36     QString fileName;//檔案名字
37     qint64 fileSize;//檔案大小
38 
39     qint64 sendSize;//已經發送檔案的大小
40     QTimer timer; //定時器,用來間隔頭檔案和檔案正文
41 };
42 
43 #endif // SERVERWIDGET_H      

clientwidget.cpp

1 #include "clientwidget.h"
  2 #include "ui_clientwidget.h"
  3 #include <QTcpSocket>
  4 #include <QByteArray>
  5 #include <QDebug>
  6 #include <QFile>
  7 #include <QMessageBox>
  8 #include <QHostAddress>
  9 #include <QIODevice>//需要包含此頭檔案,防止出現"device not open"的錯誤
 10 
 11 ClientWidget::ClientWidget(QWidget *parent) :
 12     QWidget(parent),
 13     ui(new Ui::ClientWidget)
 14 {
 15     ui->setupUi(this);
 16 
 17     setWindowTitle(QString::fromLocal8Bit("用戶端"));
 18     tcpSocket = new QTcpSocket(this);
 19     isStart = true;//剛開始時,為true
 20     ui->progressBar->setValue(0);//初始化,進度條為0
 21 
 22     //資料傳送過來成功,自動觸發QTcpSocket::readyRead信号
 23     connect(tcpSocket,&QTcpSocket::readyRead,
 24             [=]()
 25             {
 26                 //取出接收的内容
 27                 QByteArray buf = tcpSocket->readAll();
 28                 //接收頭檔案
 29                 if(isStart == true)
 30                 {
 31                     //接收頭檔案
 32                     isStart = false;
 33                     //解析頭部資訊 (檔案名##檔案大小)
 34                     //拆包
 35                     //初始化工作
 36                     fileName = QString(buf).section("##",0,0);//以"##"分割,擷取從第1部分開始,到第1部分結束的字元串
 37                     fileSize = QString(buf).section("##",1,1).toInt();
 38                     receiveSize = 0;
 39                     qDebug() << QString::fromLocal8Bit("用戶端接收的頭檔案:%1 ").arg(fileName) ;
 40 
 41                     //打開檔案,并向裡面寫内容
 42                     file.setFileName(fileName);//關聯檔案名字
 43                     bool isOK = file.open(QIODevice::WriteOnly);
 44                     if(isOK == false)//如果打開檔案失敗,中斷函數
 45                     {
 46                         qDebug() << "WriteOnly error 37" ;
 47                         tcpSocket->disconnectFromHost();//斷開連接配接
 48                         tcpSocket->close();//關閉套接字
 49                         return;
 50                     }
 51 
 52                     //彈出對話框,顯示接收檔案的資訊
 53                     //QString str =  QString::fromLocal8Bit("接收的檔案:[%1  %2kb]").arg(fileName).arg(fileSize);
 54                     //QMessageBox::information(this,QString::fromLocal8Bit("檔案資訊"),str);
 55 
 56                     //設定進度條
 57                     ui->progressBar->setMinimum(0);//最小值
 58                     ui->progressBar->setMaximum(fileSize);//最大值,因為進度條最大值是int類型,防止範圍不夠用,最好除以一個數,使最大的範圍變小一些
 59                     ui->progressBar->setValue(0);//目前值
 60 
 61                 }
 62                 //接收檔案正文
 63                 else
 64                 {
 65                     //接收檔案正文
 66                     qint64 len = file.write(buf);//向新檔案中寫buf資料,并傳回寫入的資料長度
 67                     if(len > 0)
 68                     {
 69                         //計算累計接收的資料大小
 70                         receiveSize += len;
 71                         //每接收一部分資料,就向伺服器端發送一個資訊
 72                         //QString str = QString::number(receiveSize);
 73                         //tcpSocket->write( str.toUtf8().data() );
 74                     }
 75                     //更新進度條
 76                     //ui->progressBar->setValue(receiveSize);//目前值
 77                     //接收資料完成
 78                     if(receiveSize == fileSize)
 79                     {
 80                         //先給伺服器發送(接收檔案完成的資訊)
 81                         //tcpSocket->write("file done");
 82                         //彈出對話框,提示檔案接收完成
 83                         QMessageBox::information(this,"done",QString::fromLocal8Bit("檔案接收完成 50"));
 84                         file.close();//關閉檔案
 85                         tcpSocket->disconnectFromHost();//斷開連接配接
 86                         tcpSocket->close();
 87                     }
 88                 }
 89             }
 90             );
 91 }
 92 
 93 ClientWidget::~ClientWidget()
 94 {
 95     delete ui;
 96 }
 97 
 98 void ClientWidget::on_pushButton_connect_clicked()
 99 {
100     //擷取伺服器的ip和端口
101     QString ip = ui->lineEdit_ip->text();
102     qint16 port = ui->lineEdit_port->text().toInt();
103 
104     //主動和伺服器建立連接配接
105     tcpSocket->connectToHost(QHostAddress(ip),port);
106 }      

serverwidget.cpp

1 #include "serverwidget.h"
  2 #include "ui_serverwidget.h"
  3 #include <QHostAddress>
  4 #include <QFileDialog>
  5 #include <QFileInfo>
  6 #include <QDebug>
  7 #include <QTimer>
  8 #include <QIODevice>
  9 
 10 ServerWidget::ServerWidget(QWidget *parent) :
 11     QWidget(parent),
 12     ui(new Ui::ServerWidget)
 13 {
 14     ui->setupUi(this);
 15     setWindowTitle(QString::fromLocal8Bit("伺服器端口為:9999"));
 16 
 17     //兩個按鈕都不能按
 18     ui->pushButton_file->setEnabled(false);
 19     ui->pushButton_send->setEnabled(false);
 20 
 21     //監聽套接字
 22     tcpServer = new QTcpServer(this);
 23     //監聽
 24     tcpServer->listen(QHostAddress::Any,9999);
 25     //如果用戶端成功和伺服器連接配接
 26     //tcpServer會自動觸發newConnection()
 27     connect(tcpServer,&QTcpServer::newConnection,
 28             [=]()
 29             {
 30                 //取出建立好連接配接的套接字
 31                 tcpSocket = tcpServer->nextPendingConnection();
 32                 //擷取對方的ip和端口
 33                 QString ip = tcpSocket->peerAddress().toString();
 34                 quint16 port = tcpSocket->peerPort();
 35                 //QString str = QString("[%1:%2] 成功連接配接").arg(ip).arg(port);
 36                 QString str = QString::fromLocal8Bit("[%1:%2] 成功連接配接").arg(ip).arg(port);
 37                 //顯示到編輯區
 38                 ui->textEdit->setText(str);
 39 
 40                 //成功連接配接後,才能按選擇檔案按鈕
 41                 ui->pushButton_file->setEnabled(true);
 42 
 43                 //此處不能直接放在構造函數中,因為如果直接放在那,還沒配置設定tcpSocket空間,會程式異常
 44                 //如果有資料傳送成功,對方的通信套接字會觸發readyRead(),需要在對應的槽函數做接收處理
 45                 /*connect(tcpSocket,&QTcpSocket::readyRead,
 46                         [=]()
 47                         {
 48                             //從通信套接字中取出内容
 49                             QByteArray array = tcpSocket->readAll();
 50                             //如果接收到"檔案接收完畢"的信号
 51                             if(QString(array) == "file done")
 52                             {
 53                                 ui->textEdit->append(QString::fromLocal8Bit("檔案發送完畢"));
 54                                 file.close();//關閉檔案
 55                                 tcpSocket->disconnectFromHost();//斷開與用戶端的連接配接
 56                                 tcpSocket->close();
 57                             }
 58                             else
 59                             {
 60                                 qDebug() << array;
 61                             }
 62                         }
 63                         );
 64                         */
 65             }
 66             );
 67 
 68 
 69     //此處定時器隻是起到延遲傳送的作用,啟動之後,立刻關掉,執行發送檔案方法
 70     connect(&timer,&QTimer::timeout,
 71             [=]()
 72             {
 73                 //關閉定時器
 74                 timer.stop();
 75                 //發送檔案
 76                 sendDate();
 77             }
 78             );
 79 
 80 }
 81 
 82 ServerWidget::~ServerWidget()
 83 {
 84     delete ui;
 85 }
 86 //選擇檔案按鈕
 87 void ServerWidget::on_pushButton_file_clicked()
 88 {
 89     //打開檔案對話框,選擇檔案
 90     QString filePath = QFileDialog::getOpenFileName(this,"open","../");
 91     //如果選擇的檔案有效
 92     if(filePath.isEmpty() == false)
 93     {
 94         fileName.clear();
 95         fileSize = 0;
 96 
 97         //擷取檔案資訊
 98         QFileInfo info(filePath);
 99         fileName = info.fileName();
100         fileSize = info.size();//擷取檔案名字和大小
101         sendSize = 0;//發送檔案的大小
102 
103         //隻讀方式打開
104         //指定檔案的名字
105         file.setFileName(filePath);//指定檔案,然後才能打開
106         bool isOK = file.open(QIODevice::ReadOnly);
107         if(isOK == false)
108         {
109             qDebug() << QString::fromLocal8Bit("隻讀方式打開檔案失敗 77");
110         }
111         //在編輯區提示打開檔案的路徑
112         ui->textEdit->append(filePath);
113         //把選擇檔案按鈕禁用
114         ui->pushButton_file->setEnabled(false);
115         //讓發送檔案按鈕可用
116         ui->pushButton_send->setEnabled(true);
117     }
118     else
119     {
120         qDebug() << QString::fromLocal8Bit("選擇檔案出錯 59");
121     }
122 }
123 //發送檔案按鈕
124 void ServerWidget::on_pushButton_send_clicked()
125 {
126     //先發送檔案頭資訊  檔案名##檔案大小
127     QString head = QString("%1##%2").arg(fileName).arg(fileSize);
128     //發送頭部資訊
129     qint64 len = tcpSocket->write( head.toUtf8() );
130 
131     qDebug() << head.toUtf8() << len ;
132     if(len > 0)//頭部資訊發送成功
133     {
134         //發送真正的檔案
135         //防止TCP黏封包件
136         //需要通過定時器延時20ms(保證頭檔案先發送過去,然後再發送檔案正文)
137         timer.start(20);//啟動定時器
138     }
139     else
140     {
141         qDebug() << QString::fromLocal8Bit("頭部資訊發送失敗 110");
142         file.close();
143         //改變按鈕可用狀态
144         ui->pushButton_file->setEnabled(true);
145         ui->pushButton_send->setEnabled(false);
146     }
147     //再發送真正的檔案資訊
148 }
149 //發送檔案資料
150 void ServerWidget::sendDate()
151 {
152     qint64 len = 0;
153     do
154     {
155         //每次發送資料的大小(4kb)
156         char buf[4*1024] = {0};
157         len = 0;
158         //往檔案中讀資料
159         len = file.read(buf,sizeof(buf));
160         //發送資料,讀多少發多少
161         len = tcpSocket->write(buf,len);
162         //發送的資料累計
163         sendSize += len;
164     }while(len>0);
165 
166     //是否發送檔案完畢
167     if(sendSize == fileSize)
168     {
169         ui->textEdit->append(QString::fromLocal8Bit("檔案發送完畢 128"));
170         file.close();
171         //把用戶端斷開
172         tcpSocket->disconnectFromHost();
173         tcpSocket->close();
174     }
175 }      

clientwidget.ui

QT學習筆記(12) QT下的TCP通信

serverwidget.ui

QT學習筆記(12) QT下的TCP通信

轉載于:https://www.cnblogs.com/blog-ccs/p/7457870.html