QT中能夠經過TCP協議讓服務器和客戶端之間行通訊。因此下面我就圍繞服務器和客戶端來寫。服務器
這是咱們寫服務器和客戶端的具體流程:app
A、服務器: socket
1.建立QTcpServer對象
2.啓動服務器(監聽)調用成員方法listen(QHostAddress::Any,端口號)
3.當有客戶端連接時候會發送newConnection信號,觸發槽函數接受連接(獲得一個與客戶端通訊的套接字QTcpSocket)
4.QTcpsocket發送數據用成員方法write,
5.讀數據當客戶端有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據tcp
B、客戶端 : 編輯器
1.建立QTcpSocket對象
2.連接服務器connectToHost(QHostAddress("ip"),端口號)
3.QTcpsocket發送數據用成員方法write,
4.讀數據當對方有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據ide
咱們須要調用到的頭文件有兩個:函數
#include <QTcpServer>
#include <QTcpSocket>
咱們先要在工程文件中加入networkui
QT += core gui network
下面咱們來看看服務器程序步驟:this
一、初始化服務器server對象編碼
mServer = new QTcpServer();
二、啓動監聽服務器
mServer->listen(QHostAddress::Any,9988);//9988爲端口號
三、當有客戶端連接時候會發送newConnection信號,觸發槽函數接受連接(獲得一個與客戶端通訊的套接字QTcpSocket)
connect(mServer,SIGNAL(newConnection()),this,SLOT(new_client())); mSocket = mServer->nextPendingConnection();//與客戶端通訊的套接字
四、發送數據
mSocket->write(msg.toUtf8());
五、讀數據當客戶端有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據
connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_client_data()));
六、鏈接多個客戶端
//能夠實現同時讀取多個客戶端發送過來的消息 QTcpSocket *obj = (QTcpSocket*)sender();
七、檢測掉線
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 }
說完服務器那咱們繼續來看看客戶端是怎麼實現的:
一、建立QTcpSocket對象
mSocket = new QTcpSocket();
二、連接服務器connectToHost(QHostAddress("ip"),端口號),鏈接服務器ip和端口號
mSocket->connectToHost(ui->ipEdit->text(),ui->portEdit->text().toInt()); //ui->ipEdit->text():ip,ui->portEdit->text().toInt():端口號
三、發送數據
//取發送信息編輯框內容 QString msg = ui->sendEdit->toPlainText(); mSocket->write(msg.toUtf8());//轉編碼
四、檢測連接成功信號關聯槽函數
connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc()));
五、檢測掉線信號
connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis()));
六、服務器和客戶端關閉均可以使用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_H 2 #define TCPAPP_H 3 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 //獲取發送數據端的IP 49 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 else 72 { 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以下圖
此外這裏還使用到了容器,在這裏講講容器的使用
一、定義容器對象
QVector<QTcpSocket*> clients; //存儲全部在線客戶端(容器) 解釋:QTcpSocke* 容器的類型 clients 容器名
二、往容器中添加成員
//上線用戶添加到客戶列表容器 clients.append(mSocket);
三、尋找某個成員在容器中位置
int row = clients.indexOf(obj);//找到掉線對象的內容所在的行
四、從容器中刪除成員
clients.remove(row);//從容器中刪除成員