QT基于TCP網(wǎng)絡(luò)聊天室
本文實例為大家分享了QT實現(xiàn)網(wǎng)絡(luò)聊天室的具體代碼,供大家參考,具體內(nèi)容如下
1.客戶端
1.1UI設(shè)計
分兩個部分,第一部分是消息區(qū)里面包含QPlainTextEdit和QListWidget,要顯示接收的消息和在線的成員。第二部分QLineEdit發(fā)生字符。
1.2 子模塊
1.2.1 登錄界面
登錄界面主要就是要有驗證碼,防止惡意程序的攻擊。通過paintEvent畫出一個白色矩形,在白色矩形里面顯示四個不同顏色的字母以及隨機出現(xiàn)的噪點。
代碼:
QLoginDialog.h
#ifndef _QLOGINDIALOG_H_ #define _QLOGINDIALOG_H_ #include <QDialog> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QTimer> //繼承自Dialog class QLoginDialog : public QDialog { ? ? Q_OBJECT public: ? ? typedef bool (*ValFunc)(QString); private: ? ? QLabel UserLabel; ? ? QLabel PwdLabel; ? ? QLabel CaptLabel; ? ? QLineEdit UserEdit; ? ? QLineEdit PwdEdit; ? ? QLineEdit CaptEdit; ? ? QPushButton LoginBtn; ? ? QPushButton CancelBtn; ? ? QString m_user; ? ? QString m_pwd; ? ? QString m_captcha; ? ? Qt::GlobalColor* m_colors; ? ? QTimer m_timer; ? ? ValFunc m_vf; private slots: ? ? void LoginBtn_Clicked(); ? ? void CancelBtn_Clicked(); ? ? void Timer_Timeout(); protected: ? ? void paintEvent(QPaintEvent *); ? ? QString getCaptcha(); ? ? Qt::GlobalColor* getColors(); ? ? void showEvent(QShowEvent *); public: ? ? QLoginDialog(QWidget *parent = 0); ? ? QString getUser(); ? ? QString getPwd(); ? ? void setValFunc(ValFunc); ? ? ~QLoginDialog(); }; #endif
QLoginDialog.cpp
#include "QLoginDialog.h" #include <QPainter> #include <QTime> #include <QMessageBox> QLoginDialog::QLoginDialog(QWidget* parent) : QDialog(parent, Qt::WindowCloseButtonHint), ? ? UserLabel(this), PwdLabel(this), CaptLabel(this), ? ? UserEdit(this), PwdEdit(this), CaptEdit(this), ? ? LoginBtn(this), CancelBtn(this), ? ? m_vf(NULL) { ? ? UserLabel.setText("用戶名:"); ? ? UserLabel.move(20, 30); ? ? UserLabel.resize(60, 25); ? ? UserEdit.move(85, 30); ? ? UserEdit.resize(180, 25); ? ? PwdLabel.setText("密 ?碼:"); ? ? PwdLabel.move(20, 65); ? ? PwdLabel.resize(60,25); ? ? PwdEdit.move(85, 65); ? ? PwdEdit.resize(180, 25); ? ? PwdEdit.setEchoMode(QLineEdit::Password); ? ? CaptLabel.setText("驗證碼:"); ? ? CaptLabel.move(20, 100); ? ? CaptLabel.resize(60, 25); ? ? CaptEdit.move(85, 100); ? ? CaptEdit.resize(85, 25); ? ? CancelBtn.setText("取消"); ? ? CancelBtn.move(85, 145); ? ? CancelBtn.resize(85, 30); ? ? LoginBtn.setText("登錄"); ? ? LoginBtn.move(180, 145); ? ? LoginBtn.resize(85, 30); ? ? m_timer.setParent(this); ? ? setWindowTitle("登錄..."); ? ? setFixedSize(285, 205); ? ? connect(&m_timer, SIGNAL(timeout()), this, SLOT(Timer_Timeout())); ? ? connect(&LoginBtn, SIGNAL(clicked()), this, SLOT(LoginBtn_Clicked())); ? ? connect(&CancelBtn, SIGNAL(clicked()), this, SLOT(CancelBtn_Clicked())); ? ? //以時間作為種子,獲取隨機數(shù) ? ? qsrand(QTime::currentTime().second() * 1000 + QTime::currentTime().msec()); ? ? m_timer.start(100); } void QLoginDialog::LoginBtn_Clicked() { ? ? //去除空格 ? ? QString captcha = CaptEdit.text().replace(" ", ""); ?? ? ? ? //校驗驗證碼 ? ? if( m_captcha.toLower() == captcha.toLower() ) ? ? { ? ? ? ? m_user = UserEdit.text().trimmed(); ? ? ? ? m_pwd = PwdEdit.text(); ? ? ? ? if( m_user == "" ) ? ? ? ? { ? ? ? ? ? ? QMessageBox::information(this, "消息", "用戶名不能為空!"); ? ? ? ? } ? ? ? ? else if( m_pwd == "" ) ? ? ? ? { ? ? ? ? ? ? QMessageBox::information(this, "消息", "密碼不能為空!"); ? ? ? ? } ? ? ? ? else if( (m_vf != NULL) && !(m_vf(m_user))) ?//一些非法字符不可輸入 ? ? ? ? { ? ? ? ? ? ? QMessageBox::information(this, "消息", "用戶名非法,請重新輸入!"); ? ? ? ? } ? ? ? ? else ? ? ? ? { ? ? ? ? ? ? done(Accepted); ? ? ? ? } ? ? } ? ? else ? ? { ? ? ? ? QMessageBox::critical(this, "錯誤", "驗證碼輸入錯誤!"); ? ? ? ? m_captcha = getCaptcha(); ? ? ? ? CaptEdit.selectAll(); ? ? } } void QLoginDialog::setValFunc(ValFunc vf) { ? ? m_vf = vf; } void QLoginDialog::CancelBtn_Clicked() { ? ? done(Rejected); } QString QLoginDialog::getUser() { ? ? return m_user; } QString QLoginDialog::getPwd() { ? ? return m_pwd; } //獲取四個隨機的顏色 Qt::GlobalColor* QLoginDialog::getColors() { ? ? static Qt::GlobalColor colors[4]; ? ? for(int i=0; i<4; i++) ? ? { ? ? ? ? colors[i] = static_cast<Qt::GlobalColor>(2 + qrand() % 16); ? ? } ? ? return colors; } void QLoginDialog::Timer_Timeout() { ? ? //每100毫秒獲取四種顏色 ? ? m_colors = getColors(); ? ? //更新畫板 ? ? update(); } void QLoginDialog::showEvent(QShowEvent* event) { ? ? //每次顯示之前,獲取驗證碼和顏色 ? ? m_captcha = getCaptcha(); ? ? m_colors = getColors(); ? ? QDialog::showEvent(event); } void QLoginDialog::paintEvent(QPaintEvent* event) { ? ? QPainter painter(this); ?? ? ? ? //獲取一個矩形 ? ? painter.fillRect(180, 100, 84, 24, Qt::white); ? ? painter.setFont(QFont("Comic Sans MS", 12)); ? ? //填充噪點,150個點隨機顯示 ? ? for(int i=0; i<150; i++) ? ? { ? ? ? ? painter.setPen(m_colors[i%4]); ? ? ? ? painter.drawPoint(180 + qrand() % 84, 100 + qrand() % 24); ? ? } ?? ? ? ? //驗證碼四個顏色 ? ? for(int i=0; i<4; i++) ? ? { ? ? ? ? painter.setPen(m_colors[i]); ? ? ? ? painter.drawText(180 + 20 * i, 100, 20, 24, Qt::AlignCenter, QString(m_captcha[i])); ? ? } ? ? QDialog::paintEvent(event); } QString QLoginDialog::getCaptcha() { ? ? QString ret = ""; ? ? for(int i=0; i<4; i++) ? ? { ? ? ? ? int c = (qrand() % 2) ? 'a' : 'A'; ? ? ? ? ret += static_cast<QChar>(c + qrand() % 26); ? ? } ? ? return ret; } QLoginDialog::~QLoginDialog() { }
1.2.2 協(xié)議
1.2.2.1 協(xié)議的制訂
客戶端與服務(wù)端之間的操作需要用到協(xié)議,能夠方便解析客戶端需要的操作。
操作類型+數(shù)據(jù)長度+數(shù)據(jù)
TextMessage.h
#ifndef TEXTMESSAGE_H #define TEXTMESSAGE_H #include <QObject> #include <QByteArray> class TextMessage : public QObject { ? ? Q_OBJECT ? ? QString m_type; ? ? QString m_data; public: ? ? TextMessage(QObject *parent = 0); ? ? TextMessage(QString type,QString data,QObject* parent = NULL); ? ? QString type(); ? ? int length(); ? ? QString data(); ? ? QByteArray serizlize(); ? ? bool unserialize(QByteArray ba); }; #endif // TEXTMESSAGE_H
TextMessage.cpp
#include "TextMessage.h" #include <QString> #include <QDebug> TextMessage::TextMessage(QObject *parent) : QObject(parent) { ? ? m_type = ""; ? ? m_data = ""; } TextMessage::TextMessage(QString type,QString data,QObject* parent) { ? ? m_type = type.trimmed(); ? ? if(m_type.length() < 4) ? ? { ? ? ? ? m_type += QString(4-m_type.length(),' '); ? ? } ? ? m_data = data.mid(0, 15000); } QString TextMessage::type() { ? ? return m_type.trimmed(); } int TextMessage::length() { ? ? return m_data.length(); } QString TextMessage::data() { ? ? return m_data; } //把需要發(fā)送的數(shù)據(jù)轉(zhuǎn)換成協(xié)議 QByteArray TextMessage::serizlize() { ? ? QByteArray ret; ? ? QByteArray dba = m_data.toUtf8(); ? ? QString len = QString::asprintf("%X",dba.length()); ? ? if(len.length() < 4) ? ? { ? ? ? ? len += QString(4-len.length(),' '); ? ? } ? ? ret.append(m_type.toStdString().c_str(),4); ? ? ret.append(len.toStdString().c_str(),4); ? ? ret.append(dba); ? ? return ret; } //把接收的協(xié)議轉(zhuǎn)換為具體的數(shù)據(jù) bool TextMessage::unserialize(QByteArray ba) { ? ? bool ret = (ba.length() >= 8); ? ? if(ret) ? ? { ? ? ? ? QString type = QString(ba.mid(0,4)); ? ? ? ? QString len = QString(ba.mid(4,4)).trimmed(); ? ? ? ? int l = len.toInt(&ret , 16); ? ? ? ? ret = ret && (l == (ba.length() - 8)); ? ? ? ? if(ret) ? ? ? ? { ? ? ? ? ? ? m_type = type; ? ? ? ? ? ? m_data = QString(ba.mid(8)); ? ? ? ? } ? ? } ? ? return ret; }
1.2.2.2 協(xié)議裝配器
為什么需要裝配器,原因從服務(wù)端發(fā)來的數(shù)據(jù)可能是多個操作,可能出現(xiàn)粘包、數(shù)據(jù)不足一個包等情況,可以使用裝配器來進行數(shù)據(jù)的裝配。
TxtMsgAssmbler.h
#ifndef TXTMSGASSEMBLER_H #define TXTMSGASSEMBLER_H #include <QObject> #include <QQueue> #include <QSharedPointer> #include "TextMessage.h" class TxtMsgAssembler : public QObject { ? ? QQueue<char> m_queue; ? ? QString m_type; ? ? int m_length; ? ? QByteArray m_data; ? ? void clear(); ? ? QByteArray fetch(int n); ? ? bool makeTypeAndLength(); ? ? TextMessage* makeMessage(); public: ? ? TxtMsgAssembler(QObject *parent = 0); ? ? void prepare(const char* data,int len); ? ? QSharedPointer<TextMessage> assemble(); ? ? QSharedPointer<TextMessage> assemble(const char* data, int len); ? ? void reset(); }; #endif // TXTMSGASSEMBLER_H
TxtMsgAssembler.cpp
#include "TxtMsgAssembler.h" #include <QSharedPointer> TxtMsgAssembler::TxtMsgAssembler(QObject *parent) : QObject(parent) { } void TxtMsgAssembler::clear() { ? ? m_type = ""; ? ? m_data.clear(); ? ? m_length = 0; } //把數(shù)據(jù)從隊列中取出 QByteArray TxtMsgAssembler::fetch(int n) { ? ? QByteArray ret; ? ? for(int i = 0; i < n;i++) ? ? { ? ? ? ? ret.append(m_queue.dequeue()); ? ? } ? ? return ret; } //把數(shù)據(jù)放入隊列中 void TxtMsgAssembler::prepare(const char* data,int len) { ? ? if(data != NULL) ? ? { ? ? ? ? for(int i = 0; i < len; i++) ? ? ? ? { ? ? ? ? ? ? m_queue.enqueue(data[i]); ? ? ? ? } ? ? } } //把數(shù)據(jù)進行處理,識別出操作類型和獲取數(shù)據(jù)長度 bool TxtMsgAssembler::makeTypeAndLength() { ? ? bool ret = (m_queue.length() >= 8); ? ? if(ret) ? ? { ? ? ? ? QString len = ""; ? ? ? ? m_type = QString(fetch(4)); ? ? ? ? len = QString(fetch(4)); ? ? ? ? m_length = len.trimmed().toInt(&ret,16); ? ? ? ? if(!ret) ? ? ? ? { ? ? ? ? ? ? clear(); ? ? ? ? } ? ? } ? ? return ret; } //獲取數(shù)據(jù) TextMessage* TxtMsgAssembler::makeMessage() { ? ? TextMessage* ret = NULL; ? ? if(m_type != "") ? ? { ? ? ? ? int needed = m_length - m_data.length(); ? ? ? ? int n = (needed <= m_queue.length()) ? needed : m_queue.length(); ? ? ? ? m_data.append(fetch(n)); ? ? ? ? if(m_length == m_data.length()) ? ? ? ? { ? ? ? ? ? ? ret = new TextMessage(m_type, QString(m_data)); ? ? ? ? } ? ? } ? ? return ret; } QSharedPointer<TextMessage> TxtMsgAssembler::assemble(const char* data, int len) { ? ? prepare(data, len); ? ? return assemble(); } //只要從網(wǎng)絡(luò)中接收到數(shù)據(jù)就調(diào)用該函數(shù) QSharedPointer<TextMessage> TxtMsgAssembler::assemble() { ? ? TextMessage* ret = NULL; ? ? bool tryMakeMsg = false; ? ? if(m_type == "") ? ? { ? ? ? ? tryMakeMsg = makeTypeAndLength(); ? ? } ? ? else ? ? { ? ? ? ?tryMakeMsg = true; ? ? } ? ? if(tryMakeMsg) ? ? { ? ? ? ? ret = makeMessage(); ? ? } ? ? if(ret != NULL) ? ? { ? ? ? ? clear(); ? ? } ? ? return QSharedPointer<TextMessage>(ret); } void TxtMsgAssembler::reset() { }
1.2.3 TCP客戶端
客戶端使用sokect通信,要提供read、send、connect、close等接口,還要提供當(dāng)連接、關(guān)閉上服務(wù)器,要發(fā)送給服務(wù)端一些信息。
接收到信息時,要處理服務(wù)端傳入的數(shù)據(jù)。
ClientDemo.h
#ifndef CLIENTDEMO_H #define CLIENTDEMO_H #include <QObject> #include <QTcpSocket> #include "TextMessage.h" #include "TxtMsgAssembler.h" #include "txtmsghandler.h" class ClientDemo : public QObject { ? ? Q_OBJECT ? ? QTcpSocket m_client; ? ? TxtMsgAssembler m_assmbler; ? ? TxtMsgHandler *m_handler; protected slots: ? ? void onConnected(); ? ? void onDisconnected(); ? ? void onDataReady(); ? ? void onBytesWritten(qint64 bytes); public: ? ? explicit ClientDemo(QObject *parent = 0); ? ? bool connectTo(QString ip, int port); ? ? qint64 send(TextMessage& message); ? ? qint64 available(); ? ? void setHandler(TxtMsgHandler* handler); ? ? void close(); ? ? bool isValid(); signals: public slots: }; #endif // CLIENTDEMO_H
ClientDemo.cpp
#include "ClientDemo.h" #include <QSharedPointer> #include <QHostAddress> #include <QDebug> #include <QByteArray> ClientDemo::ClientDemo(QObject *parent) : QObject(parent) { ? ? //當(dāng)連接上的時,就會調(diào)用槽函數(shù)onConnected ? ? connect(&m_client,SIGNAL(connected()),this,SLOT(onConnected())); ? ?? ? ? //當(dāng)斷開連接時,就會調(diào)用onDisconnected ? ? connect(&m_client,SIGNAL(disconnected()),this,SLOT(onDisconnected())); ? ?? ? ? //接收到服務(wù)端發(fā)送來的數(shù)據(jù),調(diào)用prepare把數(shù)據(jù)保存到隊列中,調(diào)用assemble對數(shù)據(jù)進行解析 ? ? //調(diào)用m_handler->handle處理對應(yīng)發(fā)送來的操作 ? ? connect(&m_client,SIGNAL(readyRead()),this,SLOT(onDataReady())); ? ?? ? ? //發(fā)送成功后,并沒有做什么 ? ? connect(&m_client,SIGNAL(bytesWritten(qint64)),this,SLOT(onBytesWritten(qint64))); } void ClientDemo::onConnected() { ? ? if(m_handler != NULL) ? ? { ? ? ? ? TextMessage conn("CONN",m_client.peerAddress().toString() + ":" + QString::number(m_client.peerPort())); ? ? ? ? m_handler->handle(m_client,conn); ? ? } } void ClientDemo::onDisconnected() { ? ? m_assmbler.reset(); ? ? if(m_handler != NULL) ? ? { ? ? ? ? TextMessage dscn("DSCN",""); ? ? ? ? m_handler->handle(m_client,dscn); ? ? } } void ClientDemo::onDataReady() { ? ? char buf[256] = {0}; ? ? int len = 0; ? ? while((len = m_client.read(buf,sizeof(buf))) > 0 ) ? ? { ? ? ? ? QSharedPointer<TextMessage> ptm; ? ? ? ? m_assmbler.prepare(buf,len); ? ? ? ? while( (ptm = m_assmbler.assemble()) != NULL ) ? ? ? ? { ? ? ? ? ? ? if((m_handler != NULL) ?) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? //根據(jù)具體的type,處理不同的事件。 ? ? ? ? ? ? ? ? m_handler->handle(m_client, *ptm); ? ? ? ? ? ? } ? ? ? ? } ? ? } } void ClientDemo::onBytesWritten(qint64 bytes) { ? ? (void)bytes; } bool ClientDemo::connectTo(QString ip, int port) { ? ? m_client.connectToHost(ip,port); ? ? return m_client.waitForConnected(); } qint64 ClientDemo::send(TextMessage& message) { ? ? QByteArray ba = message.serizlize(); ? ?return ?m_client.write(ba.data(),ba.length()); } qint64 ClientDemo::available() { ? ?return m_client.bytesAvailable(); } void ClientDemo::close() { ? ? m_client.close(); } bool ClientDemo::isValid() { ? ? return m_client.isValid(); } void ClientDemo::setHandler(TxtMsgHandler* handler) { ? ? m_handler = handler; }
1.2.4 客戶端界面
1.在沒有登錄的時候,發(fā)送框和發(fā)送按鈕不能使用,只有登錄按鈕可以用。
2.管理員可以通過選擇群友,點擊右鍵對群友進行權(quán)限操作(禁言、恢復(fù)、封禁)。
3.被禁言、恢復(fù)、封禁的群友要出現(xiàn)提示。
4.通過選擇群友來進行私聊
5.群友上線或下線時,消息框內(nèi)要有系統(tǒng)提示和及時刷新Listwidget
6.對于非法符號,要拒絕注冊
7.當(dāng)客戶端接收到消息時,窗口要閃爍
8.按下回車可以發(fā)送消息
MainWinUI.h
#ifndef MAINWIN_H #define MAINWIN_H #include <QWidget> #include <QVBoxLayout> #include <QGroupBox> #include <QPlainTextEdit> #include <QLineEdit> #include <QPushButton> #include <QLabel> #include <QListWidget> #include "QLoginDialog.h" #include "ClientDemo.h" #include "txtmsghandler.h" #include <QMap> #include <QMenu> class MainWin : public QWidget ,public TxtMsgHandler { ? ? Q_OBJECT ? ? typedef void(MainWin::*MSGHandler)(QTcpSocket&,TextMessage&); ? ? QVBoxLayout vMainLayout; ? ? QGroupBox msgGrpBx; ? ? QListWidget listWidget; ? ? QGroupBox inputGrpBx; ? ? QPlainTextEdit msgEditor; ? ? QMenu listWidgetMenu; ? ? QLineEdit inputEdit; ? ? QPushButton logInOutBtn; ? ? QPushButton sendBtn; ? ? QLabel statusLbl; ? ? QLoginDialog loginDlg; ? ? QString m_level; ? ? ClientDemo m_client; ? ?? ? ? //用鍵值保存type類型與對應(yīng)的操作函數(shù) ? ? QMap<QString,MSGHandler> m_handlerMap; ? ? void initMember(); ? ? void initMsgGrpBx(); ? ? void initInputGrpBx(); ? ? void initListWidgetMenu(); ? ? void connectSlots(); ? ? void setCtrlEnabled(bool enabled); ? ? QString getCheckedUserId(); ? ? //對應(yīng)類型操作的函數(shù) ? ? void CONN_Handler(QTcpSocket&,TextMessage&); ? ? void DSCN_Handler(QTcpSocket&,TextMessage&); ? ? void LIOK_Handler(QTcpSocket&,TextMessage&); ? ? void LIER_Handler(QTcpSocket&,TextMessage&); ? ? void MSGA_Handler(QTcpSocket&,TextMessage&); ? ? void USER_Handler(QTcpSocket&,TextMessage&); ? ? void CTRL_Handler(QTcpSocket&,TextMessage&); private slots: ? ? void sendBtnClicked(); ? ? void logInOutBtnClicked(); ? ? void listWidgetMenuClicked(); ? ? void listWidgetContextMenu(const QPoint&); ? ?? ? ? //重寫事件過濾器,為了處理回車鍵 ? ? bool eventFilter(QObject *, QEvent *); public: ? ? MainWin(QWidget *parent = 0); ? ? void handle(QTcpSocket& obj,TextMessage& message); ? ? ~MainWin(); }; #endif // MAINWIN_H
MainWinUI.cpp
#include "MainWinUI.h" #include <QHBoxLayout> #include <QGridLayout> #include <QAction> MainWin::MainWin(QWidget *parent) ? ? : QWidget(parent) , loginDlg(this) { ? ? initMember(); ? ? initMsgGrpBx(); ? ? initInputGrpBx(); ? ? initListWidgetMenu(); ? ? connectSlots(); ? ? vMainLayout.setSpacing(10); ? ? vMainLayout.addWidget(&msgGrpBx); ? ? vMainLayout.addWidget(&inputGrpBx); ? ? setWindowTitle("R1CHIE聊天室"); ? ? setLayout(&vMainLayout); ? ? setMinimumSize(550,400); ? ? resize(550,400); } void MainWin::connectSlots() { ? ? connect(&sendBtn,SIGNAL(clicked(bool)),this,SLOT(sendBtnClicked())); ? ? connect(&logInOutBtn,SIGNAL(clicked(bool)),this,SLOT(logInOutBtnClicked())); ? ?? //對群友點擊右鍵后出現(xiàn)的菜單的槽函數(shù)連接 ? ?connect(&listWidget,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(listWidgetContextMenu(QPoint))); } void MainWin::initMsgGrpBx() { ? ? QHBoxLayout* hbl = new QHBoxLayout(); ? ? hbl->setContentsMargins(2,5,2,2); ? ? hbl->addWidget(&msgEditor,7); ? ? hbl->addWidget(&listWidget,3); ? ? msgEditor.setReadOnly(true); ? ? msgEditor.setFocusPolicy(Qt::NoFocus); ? ? listWidget.setFocusPolicy(Qt::NoFocus); ? ? listWidget.setContextMenuPolicy(Qt::CustomContextMenu); ? ? msgGrpBx.setLayout(hbl); ? ? msgGrpBx.setTitle("聊天消息"); } void MainWin::initInputGrpBx() { ? ? QGridLayout* gl = new QGridLayout(); ? ? gl->setSpacing(10); ? ? gl->addWidget(&inputEdit,0,0,1,5); ? ? gl->addWidget(&statusLbl,1,0,1,3); ? ? gl->addWidget(&logInOutBtn,1,3); ? ? gl->addWidget(&sendBtn,1,4); ? ? inputEdit.setFixedHeight(23); ? ? inputEdit.setEnabled(false); ? ? inputEdit.installEventFilter(this); ? ? statusLbl.setText("狀態(tài): 未登錄"); ? ? logInOutBtn.setFixedHeight(30); ? ? logInOutBtn.setText("登錄"); ? ? sendBtn.setFixedHeight(30); ? ? sendBtn.setText("發(fā)送"); ? ? sendBtn.setEnabled(false); ? ? inputGrpBx.setFixedHeight(100); ? ? inputGrpBx.setLayout(gl); ? ? inputGrpBx.setTitle("用戶名"); } //對群友點擊右鍵后出現(xiàn)的菜單 void MainWin::initListWidgetMenu() { ? ? QAction* act = NULL; ? ? act = listWidgetMenu.addAction("禁言",this,SLOT(listWidgetMenuClicked())); ? ? act->setObjectName("silent"); ? ? act = listWidgetMenu.addAction("恢復(fù)",this,SLOT(listWidgetMenuClicked())); ? ? act->setObjectName("recover"); ? ? act = listWidgetMenu.addAction("封禁",this,SLOT(listWidgetMenuClicked())); ? ? act->setObjectName("kick"); } void MainWin::setCtrlEnabled(bool enabled) { ? ? inputEdit.setEnabled(enabled); ? ? statusLbl.setText(enabled ? "狀態(tài): 連接成功" : "狀態(tài): 未登錄"); ? ? logInOutBtn.setText(enabled ? "退出":"登錄"); ? ? sendBtn.setEnabled(enabled); ? ? if(enabled) ? ? { ? ? ? ? inputEdit.setFocus(); ? ? } ? ? else ? ? { ? ? ? ? msgEditor.clear(); ? ? ? ? listWidget.clear(); ? ? ? ? inputEdit.clear(); ? ? } } MainWin::~MainWin() { ? ? m_client.close(); }
MainWinSlot.cpp
#include "MainWinUI.h" #include <QMessageBox> #include <QDebug> //當(dāng)出現(xiàn)以下符號時,認定為非法用戶名 static bool ValidateUserID(QString id) { ? ? bool ret = true; ? ? QString invalid = "~`!@#$%^&*()_+[]:?><,./;"; ? ? for(int i = 0; i < invalid.length(); i++) ? ? { ? ? ? ? if(id.contains(invalid[i])) ? ? ? ? { ? ? ? ? ? ? ret = false; ? ? ? ? ? ? break; ? ? ? ? } ? ? } ? ? return ret; } void MainWin::initMember() { #define MapToHandler(MSG) ?m_handlerMap.insert(#MSG,&MainWin::MSG##_Handler) ? ? //把對應(yīng)type類型的處理函數(shù),用鍵值QMap保存 ? ? MapToHandler(CONN); ? ? MapToHandler(DSCN); ? ? MapToHandler(LIOK); ? ? MapToHandler(LIER); ? ? MapToHandler(MSGA); ? ? MapToHandler(USER); ? ? MapToHandler(CTRL); ? ? m_client.setHandler(this); } //獲取listwidget選中群友 QString MainWin::getCheckedUserId() { ? ? QString ret = ""; ? ? for(int i = 0; i < listWidget.count(); i++) ? ? { ? ? ? ? QListWidgetItem *item = listWidget.item(i); ? ? ? ? if(item->checkState() == Qt::Checked) ? ? ? ? { ? ? ? ? ? ? ret += item->text() + '\r'; ? ? ? ? } ? ? } ? ? return ret; } void MainWin::sendBtnClicked() { ? ? QString input = inputEdit.text().trimmed(); ? ? if(input != "") ? ? { ? ? ? ? QString self = inputGrpBx.title(); ? ? ? ? QString text = self + ":\n" + " ? ? " + input + "\n"; ? ? ? ? QString uid = getCheckedUserId(); ? ? ? ? bool ok = true; ? ? ? ? //如果沒有選中群友,則認為是公聊 ? ? ? ? if(uid == "") ? ? ? ? { ? ? ? ? ? ? TextMessage tm("MSGA",text); ? ? ? ? ? ? ok = m_client.send(tm); ? ? ? ? } ? ? ? ? else ? ? ? ? { ?//如果選中群友,則發(fā)給對應(yīng)的群友 ? ? ? ? ? ? QString sid = (uid.indexOf(self) >= 0) ? uid : (uid + self + '\r'); ? ? ? ? ? ? TextMessage tm("MSGP",sid+text); ? ? ? ? ? ? ok = m_client.send(tm); ? ? ? ? } ? ? ? ? if(ok ) ? ? ? ? { ? ? ? ? ? ? inputEdit.clear(); ? ? ? ? } ? ? } } void MainWin::listWidgetMenuClicked() { ? ? QAction *act = dynamic_cast<QAction*>(sender()); ? ? if(act != NULL) ? ? { ? ? ? ? const QList<QListWidgetItem*>& sl = listWidget.selectedItems(); ? ? ? ? if(sl.length() > 0) ? ? ? ? { ? ? ? ? ? ? QString user = sl.at(0)->text(); ? ? ? ? ? ? QString tip = "確認對用戶[" + user + "]進行 "+ act->text() + "操作嗎?"; ? ? ? ? ? ? //管理員對群友進行權(quán)限操作 ? ? ? ? ? ? if(QMessageBox::question(this,"提示",tip,QMessageBox::Yes,QMessageBox::No) == QMessageBox::Yes) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? QString data =act->objectName() + '\r' + user; ? ? ? ? ? ? ? ? TextMessage tm("ADMN",data); ? ? ? ? ? ? ? ? m_client.send(tm); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? else ? ? ? ? { ? ? ? ? ? ? QMessageBox::information(this,"提示","請選擇用戶!"); ? ? ? ? } ? ? } } void MainWin::listWidgetContextMenu(const QPoint&) { ?? ?//只有管理員可以操作群友 ? ? if(m_level == "admin") ? ? listWidgetMenu.exec(QCursor::pos()); } void MainWin::logInOutBtnClicked() { ? ? if(!m_client.isValid()) ? ? { ? ? ? ? loginDlg.setValFunc(ValidateUserID); ? ? ? ? if(loginDlg.exec() == QDialog::Accepted) ? ? ? ? { ? ? ? ? ? ? QString usr = loginDlg.getUser().trimmed(); ? ? ? ? ? ? QString pwd = loginDlg.getPwd(); ? ? ? ? ? ? if(m_client.connectTo("127.0.0.1",8890)) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? //setCtrlEnabled(true); ?? ??? ??? ??? ?//連接服務(wù)器成功后,向服務(wù)器發(fā)送登錄的數(shù)據(jù) ? ? ? ? ? ? ? ? TextMessage tm("LGIN" , usr + '\r' + pwd); ? ? ? ? ? ? ? ? m_client.send(tm); ? ? ? ? ? ? } ? ? ? ? ? ? else ? ? ? ? ? ? { ? ? ? ? ? ? ? ? QMessageBox::critical(this,"失敗","連接不到遠程服務(wù)器"); ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? else ? ? { ? ? ? ? m_client.close(); ? ? } } void MainWin::handle(QTcpSocket& obj,TextMessage& message) { ? ? if(m_handlerMap.contains(message.type())) ? ? { ? ? ? ?MSGHandler handler = m_handlerMap.value(message.type()); ? ? ? ?(this->*handler)(obj,message); ? ? } } void MainWin::CONN_Handler(QTcpSocket& ,TextMessage& ) { } //自己或其它群友發(fā)送的消息 void MainWin::MSGA_Handler(QTcpSocket& ,TextMessage& message) { ? ? msgEditor.appendPlainText(message.data()); ?? ? ? ? //接收到信息后,窗口閃爍 ? ? activateWindow(); } //斷開連接 void MainWin::DSCN_Handler(QTcpSocket& ,TextMessage& ) { ? ? setCtrlEnabled(false); ? ? inputGrpBx.setTitle("用戶名"); ? ? m_level = ""; } //這是服務(wù)器發(fā)來的登錄成功數(shù)據(jù) void MainWin::LIOK_Handler(QTcpSocket& ,TextMessage& message) { ? ? QStringList rl = message.data().split("\r",QString::SkipEmptyParts); ? ? QString id = rl[0]; ? ? QString status = rl[1]; ? ? m_level ?= rl[2]; ?? ?//當(dāng)前為禁言狀態(tài) ? ? if(status == "slient") ? ? { ? ? ? ? setCtrlEnabled(true); ? ? ? ? inputEdit.setEnabled(false); ? ? ? ? sendBtn.setEnabled(false); ? ? ? ? inputGrpBx.setTitle(id); ? ? } ? ? //當(dāng)前為封禁狀態(tài) ? ? else if(status == "kick") ? ? { ? ? ? ? m_client.close(); ? ? ? ? QMessageBox::information(this,"提示","賬號 [" + id + "]已被封禁"); ? ? } ? ? else ? ? { ? ? ? ? setCtrlEnabled(true); ? ? ? ? inputGrpBx.setTitle(id); ? ? } } //這是登錄失敗的操作 void MainWin::LIER_Handler(QTcpSocket& ,TextMessage& ) { ? ? QMessageBox::critical(this,"錯誤","身份驗證失敗"); ? ? m_client.close(); } //每當(dāng)有群友上線或下線時,刷新listwidget列表,由客戶端發(fā)送過來 void MainWin::USER_Handler(QTcpSocket&,TextMessage& message) { ? ? QStringList users = message.data().split("\r",QString::SkipEmptyParts); ? ? ? ? //保存勾選狀態(tài) ? ? QStringList checked = getCheckedUserId().split("\r",QString::SkipEmptyParts); ? ? listWidget.clear(); ? ? //添加發(fā)送過來的用戶 ? ? for(int i = 0; i < users.length();i++) ? ? { ? ? ? ? QListWidgetItem *item = new QListWidgetItem(); ? ? ? ? if(item != NULL) ? ? ? ? { ? ? ? ? ? ? item->setText(users[i]); ? ? ? ? ? ? item->setCheckState(Qt::Unchecked); ? ? ? ? ? ? listWidget.addItem(item); ? ? ? ? } ? ? } ?? ?//勾選的狀態(tài)恢復(fù) ? ? for(int i = 0; i < listWidget.count(); i++) ? ? { ? ? ? ? QListWidgetItem* item = listWidget.item(i); ? ? ? ? for(int j = 0; j<checked.length(); j++) ? ? ? ? { ? ? ? ? ? ? if(checked.at(j) == item->text()) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? item->setCheckState(Qt::Checked); ? ? ? ? ? ? } ? ? ? ? } ? ? } } //這是由服務(wù)器發(fā)送來的數(shù)據(jù),管理員操作后的結(jié)果 void MainWin::CTRL_Handler(QTcpSocket&,TextMessage& message) { ? ? if(message.data() == "silent") ? ? { ? ? ? ? QMessageBox::information(this,"提示","你已被禁言!"); ? ? ? ? inputEdit.clear(); ? ? ? ? inputEdit.setEnabled(false); ? ? ? ? sendBtn.setEnabled(false); ? ? } ? ? else if(message.data() == "recover" ) ? ? { ? ? ? ? QMessageBox::information(this,"提示","你已被解除禁言!"); ? ? ? ? inputEdit.setEnabled(true); ? ? ? ? sendBtn.setEnabled(true); ? ? } ? ? else if(message.data() == "kick") ? ? { ? ? ? ? QMessageBox::information(this,"提示","你已被封禁!"); ? ? ? ? m_client.close(); ? ? } } //事件過濾器的重寫,處理回車鍵 bool MainWin::eventFilter(QObject *obj, QEvent *evt) { ? ? if( (obj == &inputEdit ) && (evt->type() == QEvent::KeyPress)) ? ? { ? ? ? ? QKeyEvent *key = dynamic_cast<QKeyEvent*>(evt); ? ? ? ? if(key->text() == "\r") ? ? ? ? { ? ? ? ? ? ? sendBtnClicked(); ? ? ? ? ? ? return true; ? ? ? ? } ? ? } ? ? return QWidget::eventFilter(obj,evt); }
txtmsghandler.h
#ifndef TXTMSGHANDLER_H #define TXTMSGHANDLER_H #include <QTcpSocket> #include "TextMessage.h" class TxtMsgHandler { public: ? ? virtual void handle(QTcpSocket&,TextMessage&) = 0; }; #endif // TXTMSGHANDLER_H
1.2.5 main
main.cpp
#include "MainWinUI.h" #include <QApplication> int main(int argc, char *argv[]) { ? ? QApplication a(argc, argv); ? ? MainWin w; ? ? w.show(); ? ? return a.exec(); }
2.服務(wù)端
2.1 子模塊
2.1.1 協(xié)議的訂制
與客戶端相同
?2.1.2 協(xié)議裝配器
與客戶端相同
2.1.3 TCP客戶端
1.每當(dāng)有客戶端連接進來時,要保存
2.每當(dāng)有客戶端連接或斷開時,要有系統(tǒng)消息提示
ServerDemo.h
#ifndef SERVERDEMO_H #define SERVERDEMO_H #include <QObject> #include <QTcpServer> #include <QMap> #include "TextMessage.h" #include "TxtMsgAssembler.h" #include "txtmsghandler.h" class ServerDemo : public QObject { ? ? Q_OBJECT ? ? QTcpServer m_server; ? ? QMap<QTcpSocket*,TxtMsgAssembler*> m_map; ? ? TxtMsgHandler* m_handler; public: ? ? explicit ServerDemo(QObject *parent = 0); ? ? bool start(int port); ? ? void stop(); ? ? void setHandler(TxtMsgHandler* handler); ? ? ~ServerDemo(); protected slots: ? ? void onNewconnection(); ? ? void onConnected(); ? ? void onDisconnected(); ? ? void onDataReady(); ? ? void onBytesWritten(qint64 bytes); }; #endif // SERVERDEMO_H
ServerDemo.cpp
#include "ServerDemo.h" #include "TextMessage.h" #include "TxtMsgAssembler.h" #include <QSharedPointer> #include <QHostAddress> #include <QTcpSocket> #include <QObject> #include <QDebug> ServerDemo::ServerDemo(QObject *parent) : QObject(parent) { ? ? //有新的客戶端連接 ? ? connect(&m_server,SIGNAL(newConnection()),this,SLOT(onNewconnection())); } //開始監(jiān)聽, bool ServerDemo::start(int port) { ? ? bool ret = true; ? ? if(!m_server.isListening()) ? ? { ? ? ? ? ret = m_server.listen(QHostAddress("127.0.0.1"),port); ? ? } ? ? return ret; } void ServerDemo::stop() { ? ? if(m_server.isListening()) ? ? ? ? m_server.close(); } void ServerDemo::onNewconnection() { ? ? QTcpSocket *tcp = m_server.nextPendingConnection(); ? ? //給每一個客戶端創(chuàng)建一個裝配器 ? ? TxtMsgAssembler *as = new TxtMsgAssembler(); ? ?? ? ? //保存每一個socket對應(yīng)的裝配器 ? ? m_map.insert(tcp,as); ?? ?//給該socket建立連接 ? ? connect(tcp,SIGNAL(connected()),this,SLOT(onConnected())); ? ? connect(tcp,SIGNAL(disconnected()),this,SLOT(onDisconnected())); ? ? connect(tcp,SIGNAL(readyRead()),this,SLOT(onDataReady())); ? ? connect(tcp,SIGNAL(bytesWritten(qint64)),this,SLOT(onBytesWritten(qint64))); ? ? if(m_handler != NULL) ? ? { ? ? ? ? TextMessage msg("CONN",tcp->peerAddress().toString() + ":" + QString::number(tcp->peerPort())); ? ? ? ? m_handler->handle(*tcp,msg); ? ? } } void ServerDemo::onConnected() { } void ServerDemo::onDisconnected() { ? ? //獲取斷開連接的客戶端 ? ? QTcpSocket *tcp = dynamic_cast<QTcpSocket*>(sender()); ? ?? ? ? if(tcp != NULL) ? ? { ? ? ? ? //取出對應(yīng)tcp與裝配器的映射,并且刪除該結(jié)點 ? ? ? ? m_map.take(tcp); ? ? ? ?? ? ? ? ? if(m_handler != NULL) ? ? ? ? { ? ? ? ? ? ? //調(diào)用斷開的handler函數(shù) ? ? ? ? ? ? TextMessage msg("DSCN",""); ? ? ? ? ? ? m_handler->handle(*tcp,msg); ? ? ? ? } ? ? } } void ServerDemo::onDataReady() { ? ? QTcpSocket *tcp = dynamic_cast<QTcpSocket*>(sender()); ? ? char buf[256] = {0}; ? ? int len = 0; ? ? if(tcp != NULL) ? ? { ? ? ? ? //取出tcp對應(yīng)的裝配器 ? ? ? ? TxtMsgAssembler* assembler = m_map.value(tcp); ? ? ? ? while( (len = tcp->read(buf,sizeof(buf))) > 0) ? ? ? ? { ? ? ? ? ? ? if(assembler != NULL) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ?QSharedPointer<TextMessage> ptm; ? ? ? ? ? ? ? ? ?assembler->prepare(buf,len); ? ? ? ? ? ? ? ? ?while( (ptm = assembler->assemble()) != NULL) ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ?if(m_handler != NULL) ? ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ? ?//處理對應(yīng)類型的操作 ? ? ? ? ? ? ? ? ? ? ? ? ?m_handler->handle(*tcp,*ptm); ? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? } ? ? ? ? } ? ? } } void ServerDemo::onBytesWritten(qint64 bytes) { ? ? (void)bytes; } ServerDemo::~ServerDemo() { ? ? const QObjectList& list = m_server.children(); ? ? //關(guān)閉所有連接的客戶端 ? ? for(int i = 0; i < list.length(); i++) ? ? { ? ? ? ? QTcpSocket *tcp = dynamic_cast<QTcpSocket*>(list[i]); ? ? ? ?if(tcp != NULL) ? ? ? ?{ ? ? ? ? tcp->close(); ? ? ? ?} ? ? } ? ? //對應(yīng)的裝配器也刪除 ? ? const QList<TxtMsgAssembler*>& al = m_map.values(); ? ? for(int i = 0; i < al.length(); i++) ? ? { ? ? ? ? delete al.at(i); ? ? } } void ServerDemo::setHandler(TxtMsgHandler* handler) { ? ? m_handler = handler; }
ServerHandler.cpp
#include "ServerHandler.h" #include <QDebug> #include <QMap> ServerHandler::ServerHandler() { #define MapToHandler(MSG) ?m_handlerMap.insert(#MSG,&ServerHandler::MSG##_Handler) //連接各種類型的操作函數(shù) ? ? ? MapToHandler(CONN); ? ? ? MapToHandler(DSCN); ? ? ? MapToHandler(LGIN); ? ? ? MapToHandler(MSGA); ? ? ? MapToHandler(MSGP); ? ? ? MapToHandler(ADMN); ? ? //添加管理員賬號 ? ? ? static Node admin; ? ? ? admin.id = "admin"; ? ? ? admin.pwd = ?"123"; ? ? ? admin.level = "admin"; ? ? ? m_nodeList.append(&admin); // ? ?m_handlerMap.insert("CONN",&ServerHandler::CONN_Handler); // ? ?m_handlerMap.insert("DSCN",&ServerHandler::DSCN_Handler); // ? ?m_handlerMap.insert("LGIN",&ServerHandler::LGIN_Handler); // ? ?m_handlerMap.insert("MSGA",&ServerHandler::MSGA_Handler); } //抽象出來的獲取所有在線的群友 QString ServerHandler::getOnlineUserId() { ? ? QString ret = ""; ? ? for(int i = 0; i < m_nodeList.length(); i++) ? ? { ? ? ? ? Node* n = m_nodeList.at(i); ? ? ? ? if(n->socket != NULL) ? ? ? ? { ? ? ? ? ? ? ret += n->id + '\r'; ? ? ? ? } ? ? } ? ? return ret; } void ServerHandler::handle(QTcpSocket& obj,TextMessage& message) { ? ? if(m_handlerMap.contains(message.type())) ? ? { ? ? ? ?MSGHandler handler = ?m_handlerMap.value(message.type()); ? ? ? ?(this->*handler)(obj,message); ? ? } } //發(fā)送消息給所有在線的群友 void ServerHandler::MSGA_Handler(QTcpSocket&,TextMessage& message) { ? ? sendToAllOnlineUser(message); } void ServerHandler::CONN_Handler(QTcpSocket& ,TextMessage& ) { } //接收到客戶端發(fā)來的斷開連接操作 void ServerHandler::DSCN_Handler(QTcpSocket& obj,TextMessage& ) { ? ? Node* n = NULL; ?? ?// ? ? for(int i = 0; i < m_nodeList.length();i++) ? ? { ? ? ? ?n = m_nodeList.at(i); ? ? ? ? if(n->socket == &obj) ? ? ? ? { ? ? ? ? ? ? n->socket = NULL; ? ? ? ? ? ? break; ? ? ? ? } ? ? } ? ? //發(fā)送給客戶端,客戶端用于更新在線列表 ? ? TextMessage tm("USER",getOnlineUserId()); ? ? sendToAllOnlineUser(tm); ? ? //發(fā)送給客戶端,用于顯示系統(tǒng)消息 ? ? if(n != NULL) ? ? { ? ? ? ? TextMessage tm("MSGA", "[系統(tǒng)消息]: " + n->id + "退出聊天室"); ? ? ? ? sendToAllOnlineUser(tm); ? ? } } //客戶端發(fā)送的上線數(shù)據(jù) void ServerHandler::LGIN_Handler(QTcpSocket& obj,TextMessage& message) { ? ? QString data = message.data(); ? ? int index = data.indexOf('\r'); ? ? QString id = data.mid(0,index); ? ? QString pwd = data.mid(index+1); ? ? QString result = ""; ? ? QString status =""; ? ? QString level = ""; ? ? index = -1; ? ? //遍歷是否存在該用戶 ? ? for(int i = 0; i < m_nodeList.length(); i++) ? ? { ? ? ? ?if(id == ?m_nodeList.at(i)->id) ? ? ? ?{ ? ? ? ? ? ?index = i; ? ? ? ? ? ?break; ? ? ? ?} ? ? } ? ? //如果不存在就注冊新用戶 ? ? if(index == -1) ? ? { ? ? ? ? Node* newNode = new Node(); ? ? ? ? if(newNode != NULL) ? ? ? ? { ? ? ? ? ? ? newNode->id = id; ? ? ? ? ? ? newNode->pwd = pwd; ? ? ? ? ? ? newNode->socket = &obj; ? ? ? ? ? ? m_nodeList.append(newNode); ? ? ? ? ? ? result = "LIOK"; ? ? ? ? ? ? status = newNode->status; ? ? ? ? ? ? level = newNode->level; ? ? ? ? } ? ? ? ? else ? ? ? ? { ? ? ? ? ? ? result = "LIER"; ? ? ? ? } ? ? } ? ? else //如果存在就校驗密碼 ? ? { ? ? ? ? Node* n = m_nodeList.at(index); ? ? ? ? if(pwd == n->pwd) ? ? ? ? { ? ? ? ? ? ? n->socket = &obj; ? ? ? ? ? ? result = "LIOK"; ? ? ? ? ? ? status = n->status; ? ? ? ? ? ? level = n->level; ? ? ? ? } ? ? ? ? else ? ? ? ? { ? ? ? ? ? ? result = "LIER"; ? ? ? ? } ? ? } ? ? //發(fā)送給客戶端,當(dāng)前是登錄成功還是失敗 ? ? obj.write(TextMessage(result,id + '\r' + status + '\r' + level).serizlize()); ? ? //登錄成功 ? ? if(result == "LIOK") ? ? { ? ? ? ? //發(fā)送給客戶端用于更新在線列表 ? ? ? ? TextMessage user("USER",getOnlineUserId()); ? ? ? ? sendToAllOnlineUser(user); ? ? ? ? //發(fā)送系統(tǒng)消息 ? ? ? ? TextMessage msga("MSGA", "[系統(tǒng)消息]: " + id + "進入聊天室"); ? ? ? ? sendToAllOnlineUser(msga); ? ? } } //私聊操作 void ServerHandler::MSGP_Handler(QTcpSocket&,TextMessage& message) { ? ? //分隔消息,在協(xié)議制訂時,最后被\r分開的是具體信息 ? ? QStringList tl = message.data().split("\r",QString::SkipEmptyParts); ? ? const QByteArray& ba = TextMessage("MSGA",tl.last()).serizlize(); ? ? tl.removeLast(); ? ? //遍歷用戶,查看是否存在該用戶 ? ? for(int i = 0; i < tl.length(); i++) ? ? { ? ? ? ? for(int j = 0; j < m_nodeList.length(); j++) ? ? ? ? { ? ? ? ? ? ? Node *n = m_nodeList.at(j); ?? ??? ??? ?//如果存在,就發(fā)給對應(yīng)的用戶 ? ? ? ? ? ? if( (tl[i] == n->id) && (n->socket != NULL)) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? n->socket->write(ba); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? } ? ? } } //管理員權(quán)限操作 void ServerHandler::ADMN_Handler(QTcpSocket&,TextMessage& message) { ? ? //協(xié)議制訂:第一個為操作,第二個為用戶 ? ? QStringList data = message.data().split("\r",QString::SkipEmptyParts); ? ? QString op = data[0]; ? ? QString id = data[1]; ? ? //遍歷查看用戶是否存在 ? ? for(int i = 0; i < m_nodeList.length();i++) ? ? { ? ? ? ? Node *n = m_nodeList.at(i); ? ? ? ? //如果存在,并且被操作的用戶不是管理員身份才能被操作 ? ? ? ? if( (id == n->id) && (n->socket != NULL) && (n->level != "admin")) ? ? ? ? { ? ? ? ? ? ? n->socket->write(TextMessage("CTRL",op).serizlize()); ? ? ? ? ? ? n->status = op; ? ? ? ? ? ? break; ? ? ? ? } ? ? } } //發(fā)送消息給所有在線的用戶 void ServerHandler::sendToAllOnlineUser(TextMessage& message) { ? ? const QByteArray& ba = message.serizlize(); ? ? for(int i = 0; i < m_nodeList.length();i++) ? ? { ? ? ? ? Node* n = m_nodeList.at(i); ? ? ? ? if(n->socket != NULL) ? ? ? ? { ? ? ? ? ? ? n->socket->write(ba); ? ? ? ? } ? ? } }
2.1.4 main
main.c
#include <QCoreApplication> #include "ServerHandler.h" #include "ServerDemo.h" int main(int argc, char *argv[]) { ? ? QCoreApplication a(argc, argv); ? ? ServerHandler handler; ? ? ServerDemo server; ? ? server.setHandler(&handler); ? ? //開始監(jiān)聽 ? ? server.start(8890); ? ? return a.exec(); }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C++實現(xiàn)LeetCode(144.二叉樹的先序遍歷)
這篇文章主要介紹了C++實現(xiàn)LeetCode(144.二叉樹的先序遍歷),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07c++文件監(jiān)控之FileSystemWatcher
為了監(jiān)控web程序的靜態(tài)文件是否被惡意改動,所以學(xué)習(xí)了一下FileSystemWatcher 類對文件的監(jiān)控,由于還在初級階段,這里只貼一下關(guān)于FileSystemWatcher學(xué)習(xí)的一些代碼2019-04-04C++結(jié)構(gòu)體字節(jié)對齊和共用體大小
這篇文章主要介紹了C++結(jié)構(gòu)體字節(jié)對齊和共用體大小,結(jié)構(gòu)體內(nèi)存對齊在筆試和面試中經(jīng)常被問到,所以這篇文章做個總結(jié),首先通過代碼驗證不同結(jié)構(gòu)體的內(nèi)存大小,需要的朋友可以參考下2021-11-11