QT基于TCP网络聊天室
文章目录
1.客户端
1.1UI设计
分两个部分,第一部分是消息区里面包含QPlainTextEdit
和QListWidget
,要显示接收的消息和在线的成员。第二部分QLineEdit发生字符。
1.2 子模块
1.2.1 登录界面
登录界面主要就是要有验证码,防止恶意程序的攻击。通过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.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(&r