本文实例为大家分享了QT实现网络聊天室的具体代码,供大家参考,具体内容如下1.客户端1.1UI设计分两个部分,第一部分是消息区里面包含QPlainTextEdit和QListWidget,要显示接收的...

本文实例为大家分享了QT实现网络聊天室的具体代码,供大家参考,具体内容如下

1.客户端

1.1UI设计

分两个部分,第一部分是消息区里面包含QPlainTextEdit和QListWidget,要显示接收的消息和在线的成员。第二部分QLineEdit发生字符。

1.2 子模块

登录界面主要就是要有验证码,防止恶意程序的攻击。通过paintEvent画出一个白色矩形,在白色矩形里面显示四个不同颜色的字母以及随机出现的噪点。

代码:

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()));

  //以时间作为种子,获取随机数
  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.1 协议的制订

客户端与服务端之间的操作需要用到协议,能够方便解析客户端需要的操作。

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;
}

//把需要发送的数据转换成协议
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;
}

//把接收的协议转换为具体的数据
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 协议装配器

为什么需要装配器,原因从服务端发来的数据可能是多个操作,可能出现粘包、数据不足一个包等情况,可以使用装配器来进行数据的装配。

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;

}

//把数据从队列中取出
QByteArray TxtMsgAssembler::fetch(int n)
{
  QByteArray ret;

  for(int i = 0; i < n;i++)
  {
    ret.append(m_queue.dequeue());
  }

  return ret;

}

//把数据放入队列中
void TxtMsgAssembler::prepare(const char* data,int len)
{
  if(data != NULL)
  {
    for(int i = 0; i < len; i++)
    {
      m_queue.enqueue(data[i]);
    }
  }
}

//把数据进行处理,识别出操作类型和获取数据长度
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;
}

//获取数据
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();
}

//只要从网络中接收到数据就调用该函数
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()
{

}

客户端使用sokect通信,要提供read、send、connect、close等接口,还要提供当连接、关闭上服务器,要发送给服务端一些信息。

接收到信息时,要处理服务端传入的数据。

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)
{
  //当连接上的时,就会调用槽函数onConnected
  connect(&m_client,SIGNAL(connected()),this,SLOT(onConnected()));
 
  //当断开连接时,就会调用onDisconnected
  connect(&m_client,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
 
  //接收到服务端发送来的数据,调用prepare把数据保存到队列中,调用assemble对数据进行解析
  //调用m_handler->handle处理对应发送来的操作
  connect(&m_client,SIGNAL(readyRead()),this,SLOT(onDataReady()));
 
  //发送成功后,并没有做什么
  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) )
      {
        //根据具体的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.管理员可以通过选择群友,点击右键对群友进行权限操作(禁言、恢复、封禁)。

3.被禁言、恢复、封禁的群友要出现提示。

4.通过选择群友来进行私聊

5.群友上线或下线时,消息框内要有系统提示和及时刷新Listwidget

6.对于非法符号,要拒绝注册

7.当客户端接收到消息时,窗口要闪烁

8.按下回车可以发送消息

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类型与对应的操作函数
  QMap<QString,MSGHandler> m_handlerMap;

  void initMember();
  void initMsgGrpBx();
  void initInputGrpBx();
  void initListWidgetMenu();


  void connectSlots();

  void setCtrlEnabled(bool enabled);
  QString getCheckedUserId();

  //对应类型操作的函数
  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()));
 
//对群友点击右键后出现的菜单的槽函数连接
 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("状态: 未登录");
  logInOutBtn.setFixedHeight(30);
  logInOutBtn.setText("登录");
  sendBtn.setFixedHeight(30);
  sendBtn.setText("发送");
  sendBtn.setEnabled(false);

  inputGrpBx.setFixedHeight(100);
  inputGrpBx.setLayout(gl);
  inputGrpBx.setTitle("用户名");
}

//对群友点击右键后出现的菜单
void MainWin::initListWidgetMenu()
{
  QAction* act = NULL;

  act = listWidgetMenu.addAction("禁言",this,SLOT(listWidgetMenuClicked()));
  act->setObjectName("silent");

  act = listWidgetMenu.addAction("恢复",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 ? "状态: 连接成功" : "状态: 未登录");
  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>

//当出现以下符号时,认定为非法用户名
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)

  //把对应type类型的处理函数,用键值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
    { //如果选中群友,则发给对应的群友
      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() + "操作吗?";
      //管理员对群友进行权限操作
      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);
    //连接服务器成功后,向服务器发送登录的数据
        TextMessage tm("LGIN" , usr + '\r' + pwd);
        m_client.send(tm);

      }
      else
      {
        QMessageBox::critical(this,"失败","连接不到远程服务器");
      }
    }
  }
  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& )
{

}

//自己或其它群友发送的消息
void MainWin::MSGA_Handler(QTcpSocket& ,TextMessage& message)
{
  msgEditor.appendPlainText(message.data());
 
  //接收到信息后,窗口闪烁
  activateWindow();
}

//断开连接
void MainWin::DSCN_Handler(QTcpSocket& ,TextMessage& )
{
  setCtrlEnabled(false);
  inputGrpBx.setTitle("用户名");

  m_level = "";
}

//这是服务器发来的登录成功数据
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];
 //当前为禁言状态
  if(status == "slient")
  {
    setCtrlEnabled(true);
    inputEdit.setEnabled(false);
    sendBtn.setEnabled(false);
    inputGrpBx.setTitle(id);

  }
  //当前为封禁状态
  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();
}

//每当有群友上线或下线时,刷新listwidget列表,由客户端发送过来
void MainWin::USER_Handler(QTcpSocket&,TextMessage& message)
{
  QStringList users = message.data().split("\r",QString::SkipEmptyParts);
 
  //保存勾选状态
  QStringList checked = getCheckedUserId().split("\r",QString::SkipEmptyParts);

  listWidget.clear();

  //添加发送过来的用户
  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);
    }
  }

 //勾选的状态恢复
  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);
      }
    }
  }
}

//这是由服务器发送来的数据,管理员操作后的结果
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

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.服务端

2.1 子模块

与客户端相同

与客户端相同

1.每当有客户端连接进来时,要保存

2.每当有客户端连接或断开时,要有系统消息提示

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()));
}

//开始监听,
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();
  //给每一个客户端创建一个装配器
  TxtMsgAssembler *as = new TxtMsgAssembler();
 
  //保存每一个socket对应的装配器
  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)
  {
    //取出对应tcp与装配器的映射,并且删除该结点
    m_map.take(tcp);
   
    if(m_handler != NULL)
    {
      //调用断开的handler函数
      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对应的装配器
    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)
          {
            //处理对应类型的操作
            m_handler->handle(*tcp,*ptm);

          }
        }

      }

    }
  }

}

void ServerDemo::onBytesWritten(qint64 bytes)
{
  (void)bytes;
}


ServerDemo::~ServerDemo()
{
  const QObjectList& list = m_server.children();

  //关闭所有连接的客户端
  for(int i = 0; i < list.length(); i++)
  {
    QTcpSocket *tcp = dynamic_cast<QTcpSocket*>(list[i]);
   if(tcp != NULL)
   {
    tcp->close();
   }
  }

  //对应的装配器也删除
  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)
//连接各种类型的操作函数
   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);

  }


}

//发送消息给所有在线的群友
void ServerHandler::MSGA_Handler(QTcpSocket&,TextMessage& message)
{
  sendToAllOnlineUser(message);
}

void ServerHandler::CONN_Handler(QTcpSocket& ,TextMessage& )
{

}

//接收到客户端发来的断开连接操作
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;
    }
  }

  //发送给客户端,客户端用于更新在线列表
  TextMessage tm("USER",getOnlineUserId());
  sendToAllOnlineUser(tm);

  //发送给客户端,用于显示系统消息
  if(n != NULL)
  {
    TextMessage tm("MSGA", "[系统消息]: " + n->id + "退出聊天室");
    sendToAllOnlineUser(tm);
  }
}

//客户端发送的上线数据
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";
    }
  }
  //发送给客户端,当前是登录成功还是失败
  obj.write(TextMessage(result,id + '\r' + status + '\r' + level).serizlize());

  //登录成功
  if(result == "LIOK")
  {
    //发送给客户端用于更新在线列表
    TextMessage user("USER",getOnlineUserId());
    sendToAllOnlineUser(user);

    //发送系统消息
    TextMessage msga("MSGA", "[系统消息]: " + id + "进入聊天室");
    sendToAllOnlineUser(msga);
  }
}

//私聊操作
void ServerHandler::MSGP_Handler(QTcpSocket&,TextMessage& message)
{
  //分隔消息,在协议制订时,最后被\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);
   //如果存在,就发给对应的用户
      if( (tl[i] == n->id) && (n->socket != NULL))
      {
        n->socket->write(ba);
        break;
      }
    }

  }

}

//管理员权限操作
void ServerHandler::ADMN_Handler(QTcpSocket&,TextMessage& message)
{
  //协议制订:第一个为操作,第二个为用户
  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;
    }

  }

}

//发送消息给所有在线的用户
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);
    }
  }

}

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);
  //开始监听
  server.start(8890);

  return a.exec();
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。