QT之TCP通信
QT中可以通過TCP協議讓服務器和客戶端之間行通信。所以下面我就圍繞服務器和客戶端來寫。
這是我們寫服務器和客戶端的具體流程:
A、服務器:? ? ??
? ? ? ? ?1.創建QTcpServer對象
? ? ? ? ?2.啟動服務器(監聽)調用成員方法listen(QHostAddress::Any,端口號)
? ? ? ? ?3.當有客戶端鏈接時候會發送newConnection信號,觸發槽函數接受鏈接(得到一個與客戶端通信的套接字QTcpSocket)
? ? ? ? ?4.QTcpsocket發送數據用成員方法write,
? ? ? ? ?5.讀數據當客戶端有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據
B、客戶端? :??
? ? ? ??1.創建QTcpSocket對象
? ? ? ? 2.鏈接服務器connectToHost(QHostAddress("ip"),端口號)
? ? ? ? 3.QTcpsocket發送數據用成員方法write,
? ? ? ? 4.讀數據當對方有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據
?
我們需要調用到的頭文件有兩個:
#include <QTcpServer> #include <QTcpSocket>我們先要在工程文件中加入network
QT += core gui network下面我們來看看服務器程序步驟:
1、初始化服務器server對象
mServer = new QTcpServer();2、啟動監聽服務器
mServer->listen(QHostAddress::Any,9988);//9988為端口號3、當有客戶端鏈接時候會發送newConnection信號,觸發槽函數接受鏈接(得到一個與客戶端通信的套接字QTcpSocket)
connect(mServer,SIGNAL(newConnection()),this,SLOT(new_client()));mSocket = mServer->nextPendingConnection();//與客戶端通信的套接字4、發送數據
mSocket->write(msg.toUtf8());5、讀數據當客戶端有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據
connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_client_data()));6、連接多個客戶端
//可以實現同時讀取多個客戶端發送過來的消息QTcpSocket *obj = (QTcpSocket*)sender();7、檢測掉線
connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis())); //檢測掉線信號下面是服務器的實現的具體代碼:
1 #include "tcpserver.h"2 #include "ui_tcpserver.h"3 #include <QDebug>4 TcpServer::TcpServer(QWidget *parent) :5 QMainWindow(parent),6 ui(new Ui::TcpServer)7 {8 ui->setupUi(this);9 //初始化服務器server對象 10 mServer = new QTcpServer(); 11 //關聯客戶端連接信號newConnection 12 connect(mServer,SIGNAL(newConnection()),this,SLOT(new_client())); //連接客戶端 13 //啟動服務器監聽 14 mServer->listen(QHostAddress::Any,9988); 15 16 } 17 18 TcpServer::~TcpServer() 19 { 20 delete ui; 21 } 22 23 void TcpServer::new_client() 24 { 25 qDebug()<<"新客戶段連接"; 26 mSocket = mServer->nextPendingConnection();//與客戶端通信的套接字 27 //關聯接收客戶端數據信號readyRead信號(客戶端有數據就會發readyRead信號) 28 connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_client_data())); 29 //檢測掉線信號 30 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis())); 31 32 } 33 34 void TcpServer::read_client_data() 35 { 36 //可以實現同時讀取多個客戶端發送過來的消息 37 QTcpSocket *obj = (QTcpSocket*)sender(); 38 QString msg = obj->readAll(); 39 qDebug()<<msg; 40 } 41 42 void TcpServer::client_dis() 43 { 44 QTcpSocket *obj = (QTcpSocket*)sender();//掉線對象 45 qDebug()<<obj->peerAddress().toString();//打印出掉線對象的ip 46 }?
說完服務器那我們繼續來看看客戶端是怎么實現的:
1、創建QTcpSocket對象
mSocket = new QTcpSocket();2、鏈接服務器connectToHost(QHostAddress("ip"),端口號),連接服務器ip和端口號
mSocket->connectToHost(ui->ipEdit->text(),ui->portEdit->text().toInt()); //ui->ipEdit->text():ip,ui->portEdit->text().toInt():端口號3、發送數據
//取發送信息編輯框內容 QString msg = ui->sendEdit->toPlainText(); mSocket->write(msg.toUtf8());//轉編碼4、檢測鏈接成功信號關聯槽函數
connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc()));5、檢測掉線信號
connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis()));6、服務器和客戶端關閉都可以使用close
mSocket->close();這是客戶端實現的具體代碼
1 #include "tcpclient.h"2 #include "ui_tcpclient.h"3 #include <QDebug>4 TcpClient::TcpClient(QWidget *parent) :5 QMainWindow(parent),6 ui(new Ui::TcpClient)7 {8 ui->setupUi(this);9 //初始化套接字對象 10 mSocket = new QTcpSocket(); 11 //關聯數據信號 12 connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_data())); 13 14 } 15 16 TcpClient::~TcpClient() 17 { 18 delete ui; 19 } 20 21 void TcpClient::read_data() 22 { 23 QString msg = mSocket->readAll(); 24 qDebug()<<msg; 25 } 26 27 void TcpClient::on_btn_connectServer_clicked() 28 { 29 //檢測鏈接成功信號關聯槽函數 30 connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc())); 31 //檢測掉線信號 32 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis())); 33 //連接服務器,設置ip和端口號 34 mSocket->connectToHost(ui->ipEdit->text(),ui->portEdit->text().toInt()); 35 36 } 37 38 void TcpClient::on_btn_send_clicked() 39 { 40 //取發送信息編輯框內容 41 QString msg = ui->sendEdit->toPlainText(); 42 mSocket->write(msg.toUtf8());//轉編碼 43 } 44 45 void TcpClient::connect_suc() 46 { 47 ui->btn_connectServer->setEnabled(false);//如果連接成功則連接按鈕不能按下 48 } 49 void TcpClient::client_dis() 50 { 51 ui->btn_connectServer->setEnabled(true);//如果連接沒有成功則連接按鈕還可以按下 52 }?
這是服務器和客戶端分開兩個文件夾寫的程序,在這里我也實現了服務器和客戶端寫在同一個文件中
具體代碼如下:
頭文件:tcpapp.h
1 #ifndef TCPAPP_H2 #define TCPAPP_H3 4 #include <QMainWindow>5 #include <QTcpServer>6 #include <QTcpSocket>7 #include <QHostAddress>8 #include <QFile>9 #include <QTimer> 10 #include <QMessageBox> 11 namespace Ui { 12 class TcpApp; 13 } 14 15 class TcpApp : public QMainWindow 16 { 17 Q_OBJECT 18 19 public: 20 explicit TcpApp(QWidget *parent = 0); 21 ~TcpApp(); 22 23 private slots: 24 void on_severRB_clicked();//選擇作為服務器 25 26 void on_clientRB_clicked();//選擇作為客戶端 27 28 void on_StartBt_clicked();//啟動服務器或鏈接客戶端 29 30 void on_closeBt_clicked();//關閉服務器或斷開客戶端 31 32 void on_onlineUserList_doubleClicked(const QModelIndex &index);//選擇給哪個客戶端發送數據 33 34 void on_autoCB_clicked(bool checked);//選擇自動發送還是手動發送 35 36 void on_sendMsgBt_clicked();//發送信息 37 38 //服務器 39 void accept_connect();//與newconnection信號關聯 40 void recv_data(); //接收數據 41 42 void auto_time_send();//定時器定時發送數據 43 44 void client_disconnect();//關聯掉線信號 45 void connect_suc();//檢測客戶端連接成功信號 46 47 void on_clearRcvBt_clicked(); 48 49 void on_clearSendBt_clicked(); 50 51 private: 52 Ui::TcpApp *ui; 53 QTimer *mTimer;//定時發送數據 54 QTcpServer *mServer; 55 QTcpSocket *mSocket; 56 QVector<QTcpSocket*> clients; //存儲所有在線客戶端(容器) 57 58 bool isServer;//標志位,true為服務器,false為客戶端 59 60 //保存接收和發送數據的字節數 61 quint64 recvSize; 62 quint64 sendSize; 63 64 qint16 onNum; 65 bool isCheckServer;//判斷是否選擇了服務器 66 bool isCheckClient;//判斷是否選擇了客戶端 67 68 69 }; 70 71 #endif // TCPAPP_H源文件:tcpapp.cpp
1 #include "tcpapp.h"2 #include "ui_tcpapp.h"3 4 TcpApp::TcpApp(QWidget *parent) :5 QMainWindow(parent),6 ui(new Ui::TcpApp),7 onNum(0)8 {9 ui->setupUi(this);10 recvSize = 0;11 sendSize = 0;12 //初始化定時器13 mTimer = new QTimer();14 connect(mTimer,SIGNAL(timeout()),this,SLOT(auto_time_send()));15 }16 17 TcpApp::~TcpApp()18 {19 delete ui;20 }21 22 //與newconnection信號關聯23 void TcpApp::accept_connect()24 {25 mSocket = mServer->nextPendingConnection(); //返回與客戶端連接通信的套接字26 27 //關聯接收數據信號28 connect(mSocket,SIGNAL(readyRead()),this,SLOT(recv_data()));29 //關聯掉線信號30 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_disconnect()));31 32 //上線用戶添加到客戶列表容器33 clients.append(mSocket);34 //把用戶添加到界面列表中35 QString ip = mSocket->peerAddress().toString().remove("::ffff:");//去除客戶端中多余的字符36 ui->onlineUserList->addItem(ip);37 38 //在線數量添加39 onNum++;40 ui->onlineUserCount->setText(QString::number(onNum));//顯示在線數41 42 }43 44 //接收數據45 void TcpApp::recv_data()46 {47 QTcpSocket *obj = (QTcpSocket*)sender();48 //獲取發送數據端的IP49 QString ip = obj->peerAddress().toString();50 ip.remove("::ffff:");51 QString msg = obj->readAll();52 ui->receiveList->addItem(ip+":"+msg);//顯示接收到的數據53 recvSize += msg.size();//統計接收到的數據的字節數54 ui->receiveNumLabel->setText(QString::number(recvSize));55 }56 57 void TcpApp::client_disconnect()58 {59 QTcpSocket *obj = (QTcpSocket*)sender();//獲取掉線對象60 if(isServer)61 {62 int row = clients.indexOf(obj);//找到掉線對象的內容所在的行63 QListWidgetItem *item = ui->onlineUserList->takeItem(row);//從界面列表中去除找到的一行內容64 delete item;65 clients.remove(row);//從容器中刪除對象66 67 //掉線時刪除在線數量68 onNum--;69 ui->onlineUserCount->setText(QString::number(onNum));70 }71 else72 {73 ui->StartBt->setEnabled(true);//斷開連接的時候重新啟用開始按鈕74 }75 }76 77 78 //客戶端連接成功79 void TcpApp::connect_suc()80 {81 ui->StartBt->setEnabled(false);//連接成功則禁用開始按鈕82 }83 //定時器定時發送數據84 void TcpApp::auto_time_send()85 {86 quint64 len = mSocket->write(ui->sendMsgEdit->toPlainText().toUtf8());87 if(len > 0)88 {89 sendSize += len;//統計發送的字節數90 ui->sendNumLabel->setText(QString::number(sendSize));//把發送的字節數顯示到sendNumLabel上91 92 }93 }94 95 //選擇作為服務器96 void TcpApp::on_severRB_clicked()97 {98 this->isCheckServer = true;99 this->isServer = true; 100 //獲取本地ip顯示在IpEdit中 101 ui->IpEdit->setText(QHostAddress(QHostAddress::LocalHost).toString()); 102 ui->IpEdit->setEnabled(false);//關閉ip輸入編輯器 103 this->isCheckClient = false; 104 105 } 106 107 //選擇作為客戶端 108 void TcpApp::on_clientRB_clicked() 109 { 110 this->isCheckClient = true; 111 this->isServer = false; 112 ui->IpEdit->setEnabled(true);//打開ip輸入編輯器 113 this->isCheckServer = false; 114 115 } 116 117 //啟動服務器或者鏈接服務器 118 void TcpApp::on_StartBt_clicked() 119 { 120 if(isServer) //服務器 121 { 122 mServer = new QTcpServer(); 123 //關聯新客戶端鏈接信號 124 connect(mServer,SIGNAL(newConnection()),this,SLOT(accept_connect())); 125 mServer->listen(QHostAddress::Any,ui->PortEdit->text().toInt());//啟動服務器監聽 126 ui->StartBt->setEnabled(false);//開始按鈕禁用 127 } 128 if(isServer == false) //客戶端 129 { 130 mSocket = new QTcpSocket(); 131 //檢測鏈接成功信號 132 connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc())); 133 //設置服務器的 ip和端口號 134 mSocket->connectToHost(ui->IpEdit->text(),ui->PortEdit->text().toInt()); 135 136 137 //關聯接收數據信號 138 connect(mSocket,SIGNAL(readyRead()),this,SLOT(recv_data())); 139 //關聯掉線信號 140 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_disconnect())); 141 } 142 143 if(isCheckServer == false && isCheckClient == false)//如果兩個都沒選擇 144 { 145 QMessageBox::warning(this,"提示","請選擇服務器或者客戶端"); 146 ui->StartBt->setEnabled(true); 147 return; 148 } 149 150 if(isCheckServer)//選擇了服務器 151 { 152 if(ui->PortEdit->text().isEmpty() || ui->PortEdit->text() == "請輸入端口號") 153 { 154 QMessageBox::warning(this,"提示","請輸入端口號"); 155 ui->StartBt->setEnabled(true); 156 return; 157 } 158 } 159 160 if(isCheckClient)//選擇了客戶端 161 { 162 if(ui->IpEdit->text().isEmpty() || ui->IpEdit->text() == "請輸入ip" || ui->IpEdit->text() == "請輸入端口號") 163 { 164 QMessageBox::warning(this,"提示","請輸入ip和端口號"); 165 ui->StartBt->setEnabled(true); 166 return; 167 } 168 } 169 170 } 171 172 //關閉服務器或者斷開 173 void TcpApp::on_closeBt_clicked() 174 { 175 if(isServer)//服務器 176 { 177 for(int i=0;i<clients.count();i++) 178 { 179 clients.at(i)->close();//關閉所有客戶端 180 } 181 182 //關閉所有服務器之后開始按鈕才能啟用 183 mServer->close(); 184 ui->StartBt->setEnabled(true); 185 } 186 else //客戶端 187 { 188 mSocket->close();//關閉客戶端 189 ui->StartBt->setEnabled(true);//啟用開始按鈕 190 } 191 192 } 193 194 //雙擊選擇要發送的客戶端 195 void TcpApp::on_onlineUserList_doubleClicked(const QModelIndex &index) 196 { 197 mSocket = clients.at(index.row()); 198 199 } 200 201 //自動發送數據 202 void TcpApp::on_autoCB_clicked(bool checked) 203 { 204 if(checked) 205 206 { 207 if(ui->autoTimeEdit->text().toInt() <= 0) 208 { 209 QMessageBox::warning(this,"提示","請輸入時間值ms"); 210 ui->autoCB->setChecked(false);//把按鈕重新置于沒選中的狀態 211 return; 212 } 213 mTimer->start(ui->autoTimeEdit->text().toInt());//啟動定時器 214 } 215 else 216 { 217 mTimer->stop();//停止定時器 218 } 219 220 } 221 222 //手動發送數據 223 void TcpApp::on_sendMsgBt_clicked() 224 { 225 auto_time_send(); 226 227 } 228 229 //清空接收區 230 void TcpApp::on_clearRcvBt_clicked() 231 { 232 ui->receiveNumLabel->clear(); 233 this->recvSize = 0; 234 ui->receiveNumLabel->setText(QString::number(recvSize)); 235 } 236 237 //清空發送區 238 void TcpApp::on_clearSendBt_clicked() 239 { 240 ui->sendNumLabel->clear(); 241 this->sendSize = 0; 242 ui->sendNumLabel->setText(QString::number(sendSize)); 243 }界面文件tcpapp.ui如下圖
?
此外這里還使用到了容器,在這里講講容器的使用
1、定義容器對象
QVector<QTcpSocket*> clients; //存儲所有在線客戶端(容器)解釋:QTcpSocke* 容器的類型clients 容器名2、往容器中添加成員
//上線用戶添加到客戶列表容器clients.append(mSocket);3、尋找某個成員在容器中位置
int row = clients.indexOf(obj);//找到掉線對象的內容所在的行4、從容器中刪除成員
clients.remove(row);//從容器中刪除成員總結
- 上一篇: 技术这东西,不可不看,不可全看.
- 下一篇: 使用GCC生成无格式二进制文件(plai