1、TCP通訊過程服務器
(1)服務器端:app
服務器端有QTcpServer的監聽套接字,運用listen()方法監聽網卡的ip和端口。tcp
若是有新的鏈接傳過來,而且鏈接成功,服務器會觸發newConnection(),經過槽函數取出鏈接過來的通訊套接字QTcpSocket函數
若是有數據成功傳送過來,對方的通訊套接字QTcpSocket會觸發readyRead(),經過槽函數能夠對接收的數據進行處理ui
(2)客戶端this
首先根據ip和端口,經過通訊套接字QTcpSocket的connectToHost()方法主動和服務器創建鏈接spa
若是鏈接成功,通訊套接字QTcpSocket會自動觸發connected(),經過槽函數能夠進行操做指針
若是有數據成功傳送過來,對方的通訊套接字QTcpSocket會觸發readyRead(),經過槽函數能夠對接收的數據進行處理code
2、實例代碼以下:orm
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
3、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