Qt實現(xiàn)網(wǎng)絡聊天室的示例代碼
1. 效果演示
客戶端

服務器

連接成功之后

2. 預備知識
在Qt中,實現(xiàn)網(wǎng)絡編程的方式比用C++或C實現(xiàn)要方便簡單許多,因為Qt已經(jīng)替我們封裝好了,我們會使用就可以了,然后大家還需要了解Qt 的信號槽機制,可以參考我這篇文章,Qt信號槽
2.1 QTcpServer
QTcpServer 類用于監(jiān)聽客戶端連接以及和客戶端建立連接,在使用之前先介紹一下這個類提供的一些常用 API 函數(shù):
構(gòu)造函數(shù)
QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);
給監(jiān)聽的套接字設置監(jiān)聽
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0); // 判斷當前對象是否在監(jiān)聽, 是返回true,沒有監(jiān)聽返回false bool QTcpServer::isListening() const; // 如果當前對象正在監(jiān)聽返回監(jiān)聽的服務器地址信息, 否則返回 QHostAddress::Null QHostAddress QTcpServer::serverAddress() const; // 如果服務器正在監(jiān)聽連接,則返回服務器的端口; 否則返回0 quint16 QTcpServer::serverPort() const
參數(shù):
address:通過類 QHostAddress 可以封裝 IPv4、IPv6 格式的 IP 地址,QHostAddress::Any 表示自動綁定
port:如果指定為 0 表示隨機綁定一個可用端口。
返回值:綁定成功返回 true,失敗返回 false
得到和客戶端建立連接之后用于通信的 QTcpSocket 套接字對象,它是 QTcpServer 的一個子對象,當 QTcpServer 對象析構(gòu)的時候會自動析構(gòu)這個子對象,當然也可自己手動析構(gòu),建議用完之后自己手動析構(gòu)這個通信的 QTcpSocket 對象。
QTcpSocket *QTcpServer::nextPendingConnection();
阻塞等待客戶端發(fā)起的連接請求,不推薦在單線程程序中使用,建議使用非阻塞方式處理新連接,即使用信號 newConnection() 。
bool QTcpServer::waitForNewConnection(int msec = 0, bool *timedOut = Q_NULLPTR);
參數(shù):
msec:指定阻塞的最大時長,單位為毫秒(ms)
timeout:傳出參數(shù),如果操作超時 timeout 為 true,沒有超時 timeout 為 false
2.2 QTcpServer信號
當接受新連接導致錯誤時,將發(fā)射如下信號。socketError 參數(shù)描述了發(fā)生的錯誤相關的信息
[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError);
每次有新連接可用時都會發(fā)出 newConnection () 信號。
[signal] void QTcpServer::newConnection();
2.3 QTcpSocket
QTcpSocket 是一個套接字通信類,不管是客戶端還是服務器端都需要使用。在 Qt 中發(fā)送和接收數(shù)據(jù)也屬于 IO 操作(網(wǎng)絡 IO)
構(gòu)造函數(shù)
QTcpSocket::QTcpSocket(QObject *parent = Q_NULLPTR);
連接服務器,需要指定服務器端綁定的IP和端口信息。
[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol); [virtual] void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);
在 Qt 中不管調(diào)用讀操作函數(shù)接收數(shù)據(jù),還是調(diào)用寫函數(shù)發(fā)送數(shù)據(jù),操作的對象都是本地的由 Qt 框架維護的一塊內(nèi)存。因此,調(diào)用了發(fā)送函數(shù)數(shù)據(jù)不一定會馬上被發(fā)送到網(wǎng)絡中,調(diào)用了接收函數(shù)也不是直接從網(wǎng)絡中接收數(shù)據(jù),關于底層的相關操作是不需要使用者來維護的。
接收數(shù)據(jù)
// 指定可接收的最大字節(jié)數(shù) maxSize 的數(shù)據(jù)到指針 data 指向的內(nèi)存中 qint64 QIODevice::read(char *data, qint64 maxSize); // 指定可接收的最大字節(jié)數(shù) maxSize,返回接收的字符串 QByteArray QIODevice::read(qint64 maxSize); // 將當前可用操作數(shù)據(jù)全部讀出,通過返回值返回讀出的字符串 QByteArray QIODevice::readAll();
2.4 QTcpSocket信號
在使用 QTcpSocket 進行套接字通信的過程中,如果該類對象發(fā)射出 readyRead() 信號,說明對端發(fā)送的數(shù)據(jù)達到了,之后就可以調(diào)用 read 函數(shù)接收數(shù)據(jù)了。
[signal] void QIODevice::readyRead();
調(diào)用 connectToHost() 函數(shù)并成功建立連接之后發(fā)出 connected() 信號。
在套接字斷開連接時發(fā)出 disconnected() 信號。
調(diào)用 connectToHost() 函數(shù)并成功建立連接之后發(fā)出 connected() 信號。 [signal] void QAbstractSocket::disconnected();
3. 通信流程
3.1 服務器端
- 創(chuàng)建套接字服務器 QTcpServer 對象
- 通過 QTcpServer 對象設置監(jiān)聽,即:QTcpServer::listen()
- 基于 QTcpServer::newConnection() 信號檢測是否有新的客戶端連接
- 如果有新的客戶端連接調(diào)用 QTcpSocket *QTcpServer::nextPendingConnection() 得到通信的套接字對象
- 使用通信的套接字對象 QTcpSocket 和客戶端進行通信
頭文件
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_startServer_clicked();
void on_sendMsg_clicked();
private:
Ui::MainWindow *ui;
QTcpServer* m_server;
QTcpSocket* m_tcp;
};
源文件
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle("TCP - 服務器");
// 創(chuàng)建 QTcpServer 對象
m_server = new QTcpServer(this);
// 檢測是否有新的客戶端連接
connect(m_server, &QTcpServer::newConnection, this, [=]()
{
m_tcp = m_server->nextPendingConnection();
ui->record->append("成功和客戶端建立了新的連接...");
m_status->setPixmap(QPixmap(":/connect.png").scaled(20, 20));
// 檢測是否有客戶端數(shù)據(jù)
connect(m_tcp, &QTcpSocket::readyRead, this, [=]()
{
// 接收數(shù)據(jù)
QString recvMsg = m_tcp->readAll();
ui->record->append("客戶端Say: " + recvMsg);
});
// 客戶端斷開了連接
connect(m_tcp, &QTcpSocket::disconnected, this, [=]()
{
ui->record->append("客戶端已經(jīng)斷開了連接...");
m_tcp->deleteLater();
m_status->setPixmap(QPixmap(":/disconnect.png").scaled(20, 20));
});
});
}
MainWindow::~MainWindow()
{
delete ui;
}
// 啟動服務器端的服務按鈕
void MainWindow::on_startServer_clicked()
{
unsigned short port = ui->port->text().toInt();
// 設置服務器監(jiān)聽
m_server->listen(QHostAddress::Any, port);
ui->startServer->setEnabled(false);
}
// 點擊發(fā)送數(shù)據(jù)按鈕
void MainWindow::on_sendMsg_clicked()
{
QString sendMsg = ui->msg->toPlainText();
m_tcp->write(sendMsg.toUtf8());
ui->record->append("服務器Say: " + sendMsg);
ui->msg->clear();
3.2 客戶端
通信流程
- 創(chuàng)建通信的套接字類 QTcpSocket 對象
- 使用服務器端綁定的 IP 和端口連接服務器 QAbstractSocket::connectToHost()
- 使用 QTcpSocket 對象和服務器進行通信
頭文件
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_connectServer_clicked();
void on_sendMsg_clicked();
void on_disconnect_clicked();
private:
Ui::MainWindow *ui;
QTcpSocket* m_tcp;
};
源文件
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle("TCP - 客戶端");
// 創(chuàng)建通信的套接字對象
m_tcp = new QTcpSocket(this);
// 檢測服務器是否回復了數(shù)據(jù)
connect(m_tcp, &QTcpSocket::readyRead, [=]()
{
// 接收服務器發(fā)送的數(shù)據(jù)
QByteArray recvMsg = m_tcp->readAll();
ui->record->append("服務器Say: " + recvMsg);
});
// 檢測是否和服務器是否連接成功了
connect(m_tcp, &QTcpSocket::connected, this, [=]()
{
ui->record->append("恭喜, 連接服務器成功!!!");
m_status->setPixmap(QPixmap(":/connect.png").scaled(20, 20));
});
// 檢測服務器是否和客戶端斷開了連接
connect(m_tcp, &QTcpSocket::disconnected, this, [=]()
{
ui->record->append("服務器已經(jīng)斷開了連接, ...");
ui->connectServer->setEnabled(true);
ui->disconnect->setEnabled(false);
});
}
MainWindow::~MainWindow()
{
delete ui;
}
// 連接服務器按鈕按下之后的處理動作
void MainWindow::on_connectServer_clicked()
{
QString ip = ui->ip->text();
unsigned short port = ui->port->text().toInt();
// 連接服務器
m_tcp->connectToHost(QHostAddress(ip), port);
ui->connectServer->setEnabled(false);
ui->disconnect->setEnabled(true);
}
// 發(fā)送數(shù)據(jù)按鈕按下之后的處理動作
void MainWindow::on_sendMsg_clicked()
{
QString sendMsg = ui->msg->toPlainText();
m_tcp->write(sendMsg.toUtf8());
ui->record->append("客戶端Say: " + sendMsg);
ui->msg->clear();
}
// 斷開連接按鈕被按下之后的處理動作
void MainWindow::on_disconnect_clicked()
{
m_tcp->close();
ui->connectServer->setEnabled(true);
ui->disconnect->setEnabled(false);
}
到此這篇關于Qt實現(xiàn)網(wǎng)絡聊天室的示例代碼的文章就介紹到這了,更多相關Qt 聊天室內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C++ 類的賦值運算符''''=''''重載的方法實現(xiàn)
這篇文章主要介紹了C++ 類的賦值運算符'='重載的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-02-02
C++實現(xiàn)LeetCode(33.在旋轉(zhuǎn)有序數(shù)組中搜索)
這篇文章主要介紹了C++實現(xiàn)LeetCode(33.在旋轉(zhuǎn)有序數(shù)組中搜索),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07
利用C++實現(xiàn)矩陣的相加/相稱/轉(zhuǎn)置/求鞍點
利用C++實現(xiàn)矩陣的相加/相稱/轉(zhuǎn)置/求鞍點。需要的朋友可以過來參考下,希望對大家有所幫助2013-10-10

