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
#ifndef TCPAPP_H #define TCPAPP_H #include <QMainWindow> #include <QTcpServer> #include <QTcpSocket> #include <QHostAddress> #include <QFile> #include <QTimer> #include <QMessageBox> namespace Ui { class TcpApp; } class TcpApp : public QMainWindow { Q_OBJECT public: explicit TcpApp(QWidget *parent = 0); ~TcpApp(); private slots: void on_severRB_clicked();//選擇作為服務器 void on_clientRB_clicked();//選擇作為客戶端 void on_StartBt_clicked();//啟動服務器或鏈接客戶端 void on_closeBt_clicked();//關閉服務器或斷開客戶端 void on_onlineUserList_doubleClicked(const QModelIndex &index);//選擇給哪個客戶端發送數據 void on_autoCB_clicked(bool checked);//選擇自動發送還是手動發送 void on_sendMsgBt_clicked();//發送信息 //服務器 void accept_connect();//與newconnection信號關聯 void recv_data(); //接收數據 void auto_time_send();//定時器定時發送數據 void client_disconnect();//關聯掉線信號 void connect_suc();//檢測客戶端連接成功信號 void on_clearRcvBt_clicked(); void on_clearSendBt_clicked(); private: Ui::TcpApp *ui; QTimer *mTimer;//定時發送數據 QTcpServer *mServer; QTcpSocket *mSocket; QVector<QTcpSocket*> clients; //存儲所有在線客戶端(容器) bool isServer;//標志位,true為服務器,false為客戶端 //保存接收和發送數據的字節數 quint64 recvSize; quint64 sendSize; qint16 onNum; bool isCheckServer;//判斷是否選擇了服務器 bool isCheckClient;//判斷是否選擇了客戶端 }; #endif // TCPAPP_H
源文件:tcpapp.cpp
#include "tcpapp.h" #include "ui_tcpapp.h" TcpApp::TcpApp(QWidget *parent) : QMainWindow(parent), ui(new Ui::TcpApp), onNum(0) { ui->setupUi(this); recvSize = 0; sendSize = 0; //初始化定時器 mTimer = new QTimer(); connect(mTimer,SIGNAL(timeout()),this,SLOT(auto_time_send())); } TcpApp::~TcpApp() { delete ui; } //與newconnection信號關聯 void TcpApp::accept_connect() { mSocket = mServer->nextPendingConnection(); //返回與客戶端連接通信的套接字 //關聯接收數據信號 connect(mSocket,SIGNAL(readyRead()),this,SLOT(recv_data())); //關聯掉線信號 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_disconnect())); //上線用戶添加到客戶列表容器 clients.append(mSocket); //把用戶添加到界面列表中 QString ip = mSocket->peerAddress().toString().remove("::ffff:");//去除客戶端中多余的字符 ui->onlineUserList->addItem(ip); //在線數量添加 onNum++; ui->onlineUserCount->setText(QString::number(onNum));//顯示在線數 } //接收數據 void TcpApp::recv_data() { QTcpSocket *obj = (QTcpSocket*)sender(); //獲取發送數據端的IP QString ip = obj->peerAddress().toString(); ip.remove("::ffff:"); QString msg = obj->readAll(); ui->receiveList->addItem(ip+":"+msg);//顯示接收到的數據 recvSize += msg.size();//統計接收到的數據的字節數 ui->receiveNumLabel->setText(QString::number(recvSize)); } void TcpApp::client_disconnect() { QTcpSocket *obj = (QTcpSocket*)sender();//獲取掉線對象 if(isServer) { int row = clients.indexOf(obj);//找到掉線對象的內容所在的行 QListWidgetItem *item = ui->onlineUserList->takeItem(row);//從界面列表中去除找到的一行內容 delete item; clients.remove(row);//從容器中刪除對象 //掉線時刪除在線數量 onNum--; ui->onlineUserCount->setText(QString::number(onNum)); } else { ui->StartBt->setEnabled(true);//斷開連接的時候重新啟用開始按鈕 } } //客戶端連接成功 void TcpApp::connect_suc() { ui->StartBt->setEnabled(false);//連接成功則禁用開始按鈕 } //定時器定時發送數據 void TcpApp::auto_time_send() { quint64 len = mSocket->write(ui->sendMsgEdit->toPlainText().toUtf8()); if(len > 0) { sendSize += len;//統計發送的字節數 ui->sendNumLabel->setText(QString::number(sendSize));//把發送的字節數顯示到sendNumLabel上 } } //選擇作為服務器 void TcpApp::on_severRB_clicked() { this->isCheckServer = true; this->isServer = true; //獲取本地ip顯示在IpEdit中 ui->IpEdit->setText(QHostAddress(QHostAddress::LocalHost).toString()); ui->IpEdit->setEnabled(false);//關閉ip輸入編輯器 this->isCheckClient = false; } //選擇作為客戶端 void TcpApp::on_clientRB_clicked() { this->isCheckClient = true; this->isServer = false; ui->IpEdit->setEnabled(true);//打開ip輸入編輯器 this->isCheckServer = false; } //啟動服務器或者鏈接服務器 void TcpApp::on_StartBt_clicked() { if(isServer) //服務器 { mServer = new QTcpServer(); //關聯新客戶端鏈接信號 connect(mServer,SIGNAL(newConnection()),this,SLOT(accept_connect())); mServer->listen(QHostAddress::Any,ui->PortEdit->text().toInt());//啟動服務器監聽 ui->StartBt->setEnabled(false);//開始按鈕禁用 } if(isServer == false) //客戶端 { mSocket = new QTcpSocket(); //檢測鏈接成功信號 connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc())); //設置服務器的 ip和端口號 mSocket->connectToHost(ui->IpEdit->text(),ui->PortEdit->text().toInt()); //關聯接收數據信號 connect(mSocket,SIGNAL(readyRead()),this,SLOT(recv_data())); //關聯掉線信號 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_disconnect())); } if(isCheckServer == false && isCheckClient == false)//如果兩個都沒選擇 { QMessageBox::warning(this,"提示","請選擇服務器或者客戶端"); ui->StartBt->setEnabled(true); return; } if(isCheckServer)//選擇了服務器 { if(ui->PortEdit->text().isEmpty() || ui->PortEdit->text() == "請輸入端口號") { QMessageBox::warning(this,"提示","請輸入端口號"); ui->StartBt->setEnabled(true); return; } } if(isCheckClient)//選擇了客戶端 { if(ui->IpEdit->text().isEmpty() || ui->IpEdit->text() == "請輸入ip" || ui->IpEdit->text() == "請輸入端口號") { QMessageBox::warning(this,"提示","請輸入ip和端口號"); ui->StartBt->setEnabled(true); return; } } } //關閉服務器或者斷開 void TcpApp::on_closeBt_clicked() { if(isServer)//服務器 { for(int i=0;i<clients.count();i++) { clients.at(i)->close();//關閉所有客戶端 } //關閉所有服務器之后開始按鈕才能啟用 mServer->close(); ui->StartBt->setEnabled(true); } else //客戶端 { mSocket->close();//關閉客戶端 ui->StartBt->setEnabled(true);//啟用開始按鈕 } } //雙擊選擇要發送的客戶端 void TcpApp::on_onlineUserList_doubleClicked(const QModelIndex &index) { mSocket = clients.at(index.row()); } //自動發送數據 void TcpApp::on_autoCB_clicked(bool checked) { if(checked) { if(ui->autoTimeEdit->text().toInt() <= 0) { QMessageBox::warning(this,"提示","請輸入時間值ms"); ui->autoCB->setChecked(false);//把按鈕重新置於沒選中的狀態 return; } mTimer->start(ui->autoTimeEdit->text().toInt());//啟動定時器 } else { mTimer->stop();//停止定時器 } } //手動發送數據 void TcpApp::on_sendMsgBt_clicked() { auto_time_send(); } //清空接收區 void TcpApp::on_clearRcvBt_clicked() { ui->receiveNumLabel->clear(); this->recvSize = 0; ui->receiveNumLabel->setText(QString::number(recvSize)); } //清空發送區 void TcpApp::on_clearSendBt_clicked() { ui->sendNumLabel->clear(); this->sendSize = 0; ui->sendNumLabel->setText(QString::number(sendSize)); }
界面文件tcpapp.ui如下圖
此外這里還使用到了容器,在這里講講容器的使用
1、定義容器對象
QVector<QTcpSocket*> clients; //存儲所有在線客戶端(容器) 解釋:QTcpSocke* 容器的類型 clients 容器名
2、往容器中添加成員
//上線用戶添加到客戶列表容器 clients.append(mSocket);
3、尋找某個成員在容器中位置
int row = clients.indexOf(obj);//找到掉線對象的內容所在的行
4、從容器中刪除成員
clients.remove(row);//從容器中刪除成員