一、TCP通信過程
(1)伺服器端:
伺服器端有QTcpServer的監聽套接字,運用listen()方法監聽網卡的ip和端口。
如果有新的連接配接傳過來,并且連接配接成功,伺服器會觸發newConnection(),通過槽函數取出連接配接過來的通信套接字QTcpSocket
如果有資料成功傳送過來,對方的通信套接字QTcpSocket會觸發readyRead(),通過槽函數可以對接收的資料進行處理
(2)用戶端
首先根據ip和端口,通過通信套接字QTcpSocket的connectToHost()方法主動和伺服器建立連接配接
如果連接配接成功,通信套接字QTcpSocket會自動觸發connected(),通過槽函數可以進行操作
如果有資料成功傳送過來,對方的通信套接字QTcpSocket會觸發readyRead(),通過槽函數可以對接收的資料進行處理
二、執行個體代碼如下:
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
clientwidget.ui
三、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
serverwidget.ui
轉載于:https://www.cnblogs.com/blog-ccs/p/7457870.html