Qt5.8《网络版够级》编写日志之五:客户端登陆及注册功能实现
Qt5.8《网络版够级》编写日志之五:客户端登陆及注册功能实现
做完服务器端对登陆功能的响应,现在着手把客户端登陆功能进行实现,顺道也把注册功能实现。登陆和注册主要就是将用户的登陆信息和注册信息通过TCP传给服务器端,服务器端收到信息根据用户的输入情况判断后,给予答复信息,从而完成整个信息的交互。另外,客户端相关窗口的界面初始化需要做一部分工作。
1 登陆窗口与主界面窗口交互信息及信号绑定
1.1 概述
梳理一下,SLoginAndRegistor与主界面ClientFrame之间的主要信息交互有:
A) SLoginAndRegistor将用户输入的登陆信息(姓名、密码)发给ClientFrame;
B) ClientFrame收到SLoginAndRegistor的登陆信息,加工处理后通过Tcp套接字转给服务器;
C) 收到服务器的响应信息后,ClientFrame将登陆响应信息再转给SloginAndRegistor;
D) 注册信息也是如此,SLoginAndRegistor将用户输入的注册信息(姓名、密码、头像索引)发给ClientFrame;ClientFrame收到SloginAndRegistor的注册信息,加工处理后通过Tcp套接字转给服务器;收到服务器的响应信息后,ClientFrame将登陆响应信息再转给SloginAndRegistor;
另外,由于客户端首先是从登陆注册界面开始的,只有登陆成功才能进入客户端主界面,因此实现客户端登陆注册功能,就需要完成登陆界面也就是SLoginAndRegistor与主界面ClientFrame之间的信息交互,因为Tcp套接字是建立在主界面类中的。因此,还要解决登陆不成功之前,ClientFrame窗口不显示,而登陆成功后关闭SLoginAndRegistor窗口,这之间也需要进行信息的传递。
1.2 程序入口main函数的修改
这里主要是完成没有成功登陆时,主界面窗口不现实,同时完成SLoginAndRegistor与主界面ClientFrame之间信息槽绑定,代码如下:
int main(int argc, char *argv[])
{
QApplicationa(argc, argv);
//实例化ClientFrame及SLoginAndRegistor,这里有个区别就是SLoginAndRegistor一定要是用new的方式动态实例化,而不能直接定义对象实例,因为如果也向ClientFrame那样实例化,在后续成功登陆后要关闭SLoginAndRegistor窗口,会发生关闭不了的情况,或者关闭了会出现程序告错的问题;个人理解是如果不用new的方式动态创建,在程序关闭时,会存在二次销毁的问题,出现告错,另外不用new动态创建它,即便执行它的close方法,也没法进行窗口销毁。这个试验了多次才解决。
ClientFrame w;
SLoginAndRegistor *s;
s= new SLoginAndRegistor;
//绑定SLoginAndRegistor向ClientFrame发送信息的信号槽,信息是QString类型的。
QObject::connect(s,SIGNAL(sendMsgToServer(QString)),&w,SLOT(sendMsgToServerSlot(QString)));
//绑定ClientFrame向SLoginAndRegistor发送信息的信号槽,信息也是QString类型的。
QObject::connect(&w,SIGNAL(loginAndRegInfoReturn(QString)),s,SLOT(loginAndRegInfoSlot(QString)));
//显示登陆注册窗口
s->show();
//调用了登陆注册窗口的窗口固定函数,这里这么弄是为了让注册界面中头像的大小确定,因此窗口是自拉伸的,如果不是在调用show之后再固定窗口大小,会造成头像索引以及我放置的一个Logo图片的图标非常小,个人理解它以没有拉伸的值来显示的。
s->fixedUI();
//最后没有将ClientFrame窗口调用show函数,而是在登陆成功后再调用ClientFrame的show函数将其显示,这样就实现了主界面的隐藏。这个也试了好些办法,最终这么处理
return a.exec();
}
2 登陆注册窗口SLoginAndRegistor类功能实现
完成好main函数的修改,绑定完登陆窗口SLoginAndRegistor与主界面ClientFrame之后,现在开始完善SLoginAndRegistor的具体登陆注册功能代码,这里一并将SLoginAndRegistor登陆注册窗口的界面初始化工作一起完成。
2.1 SLoginAndRegistor界面和成员初始化
涉及到的界面初始化工作,主要集中SLoginAndRegistor的构造函数中完成,为了让代码更清晰,这里将一个代码集中成初始化函数。
2.1.1 构造函数修改
直接上代码,实现见代码注释。
SLoginAndRegistor::SLoginAndRegistor(QWidget*parent) : QWidget(parent),
ui(new Ui::SLoginAndRegistor)
{
ui->setupUi(this);
//调用自己写的界面初始化方法
initUI();
//设置窗口在接收close信号时关闭,加这个也是为了其在登陆成功后可以关闭
this->setAttribute(Qt::WA_DeleteOnClose,true);
//将登陆注册窗口居中显示
QDesktopWidget*desktop = QApplication::desktop();
move((desktop->width()- this->width())/2, (desktop->height() - this->height())/2);
}
2.1.2 界面初始化方法initUI
这里主要是完成配置信息的加载,注册头像下拉框图标的加载,代码如下:
void SLoginAndRegistor::initUI()
{
//设置主界面窗口标题
this->setWindowTitle("欢迎登陆够级游戏平台!");
QString Temp;
//将服务器的IP以及端口、最近登陆用户名加载到界面控件上,这三个信息写在了配置文件里,是为了,方便用户,减少输入
ui->EdtServerIPLogin->setText(ClientConfig.ServerIP);
ui->EdtServerIPReg->setText(ClientConfig.ServerIP);
ui->EdtPlayerNameLogin->setText(ClientConfig.LastPlayer);
Temp.sprintf("%d",ClientConfig.ServerPort);
ui->EdtServerPortLogin->setText(Temp);
ui->EdtServerPortReg->setText(Temp);
//初始化头像列表
//用下拉框实现图标显示,首先要将其设置为QlistView模式
ui->CmbHeadIcon->setView(new QListView);
ui->CmbHeadIcon->setIconSize(QSize(100,80));
//然后,将图标一个个加到下拉框中,这里我选取了20副头像作为可选图标,按照01-20.png编号,加入到资源文件里。使用时只要给定其资源地址就可以用了
for(int i=1;i<=HeadIconNum;i++)
{
Temp.sprintf(":/HeadIcon/image/headIcon/%02d.png",i);
ui->CmbHeadIcon->addItem(QIcon(Temp),"");
}
}
2.1.3 窗口大小固定方法
由于我在登陆注册界面上放置了一个QPushButton用来显示一张够级的图片,而它是自动拉伸的,因此如果在构造函数里调用这段代码,图片的大小是没拉伸时的大小,因此写了这个方法,等登陆窗口显示后,再显式的调用它。
void SLoginAndRegistor::fixedUI()
{
//加载要显示的图片
QIcon button_ico(":/UI/image/UI/goge.png");
QSize size = ui->pushButton->size();
ui->pushButton->setIcon(button_ico);
ui->pushButton->setIconSize(size);
//固定登陆窗口大小,使用这个方法后,窗口的最大化按钮会消失
this->setFixedSize(this->size());
}
2.1.4 登陆注册页面初始化后登陆效果图
通过以上的完善,基本上完成了登陆注册窗口界面初始化工作,其效果如下图所示:
图一 登陆界面
图二 注册界面
2.2 SLoginAndRegistor信号定义
SLoginAndRegistor发送的信号只有void sendMsgToServer(QString Msg),通过这个信号将用户登陆或者注册的相关信息传送给主界面ClientFrame来进行处理。
2.3 SLoginAndRegistor槽实现
2.3.1 登陆按钮响应槽
这个槽函数主要用来响应用户登陆按钮事件的响应,完成对用户输入信息的检查后,按照约定的格式将登陆名、密码、服务器IP、服务器端口号,转发给ClientFrame进行登陆操作。代码如下:
voidSLoginAndRegistor::on_PsbLogin_clicked()
{
QString Msg;
//判断各类信息是否为空,这里还有个小问题需要完善,也就是防注入攻击没有进行屏蔽,这里仅进行了非空判断,其它情况没有做,后面再完善
if((ui->EdtServerIPLogin->text()!="") && (ui->EdtServerPortLogin->text()!="")
&&(ui->EdtPlayerNameLogin->text()!="") && (ui->EdtPasswordLogin->text() !=""))
{
//加工信息
Msg = "LOGIN:";
Msg += ui->EdtServerIPLogin->text() + ":";
Msg += ui->EdtServerPortLogin->text() + ":";
Msg += ui->EdtPlayerNameLogin->text() + ":";
Msg += ui->EdtPasswordLogin->text();
//通过sendMsgToServer信号将该信息转给ClientFrame处理
emit sendMsgToServer(Msg);
}
else
{
QMessageBox::warning(this,tr("提示"),"登陆信息填写不完整!",QMessageBox::Yes);
}
}
2.3.2 注册按钮响应槽
这个槽函数主要用来响应用户注册按钮事件的响应,完成对用户输入信息的检查后,按照约定的格式将登陆名、密码、服务器IP、服务器端口号以及头像索引号,转发给ClientFrame进行注册操作。代码如下:
voidSLoginAndRegistor::on_PsbRegistor_clicked()
{
//这里限制了注册用户名不能为“所有人”,因为考虑到后续公共聊天室有一个向所有人发话的下拉框,避免向个人发话,变成向所有人发话。
if(ui->EdtPlayerNameReg->text().startsWith("所有人"))
QMessageBox::warning(this,tr("提示"),"用户名不能为'所有人'!",QMessageBox::Yes);
//判断各类信息是否为空
if((ui->EdtServerIPReg->text()!="") && (ui->EdtServerPortReg->text() !="")
&&(ui->EdtPlayerNameReg->text()!="") && (ui->EdtPasswordReg->text() != ""))
{
QString Temp;
QString Msg;
//按照格式约定加工信息
Temp.sprintf("%d",ui->CmbHeadIcon->currentIndex());
Msg = "REG:";
Msg += ui->EdtServerIPReg->text() + ":";
Msg += ui->EdtServerPortReg->text() + ":";
Msg += ui->EdtPlayerNameReg->text() + ":";
Msg += ui->EdtPasswordReg->text() + ":";
Msg += Temp;
//转发信息给ClientFrame
emit sendMsgToServer(Msg);
}
else
{
QMessageBox::warning(this,tr("提示"),"注册信息填写不完整!",QMessageBox::Yes);
}
}
2.3.2 ClientFrame回馈信号信息处理槽
这个槽函数主要用来响应用户进行登陆和注册操作后,从ClientFrame回馈回来的信息处理。对于登陆成功的情况,有一个处理,而其他信息一律以MessageBox的方式显示。代码如下:
voidSLoginAndRegistor::loginAndRegInfoSlot(QString Info)
{
//显示ClientFrame窗口类返回的信息
QMessageBox::information(this,tr("提示"),Info,QMessageBox::Yes);
//如果返回的信息是登陆成功的话,关闭SLoginAndRegistor窗口
if(Info.startsWith("登陆成功!"))
this->close();
}
3 主界面窗口ClientFrame类功能实现
3.1 ClientFrame界面和成员初始化实现
主要是完成一些成员变量的初始化,以及界面一些控件的初始化,分别放在构造函数和界面初始化函数中。
3.1.1 构造函数完善
比较简单,直接上代码:
ClientFrame::ClientFrame(QWidget *parent): QWidget(parent), ui(new Ui::ClientFrame)
{
ui->setupUi(this);
//初始化套接字
ClientSocket =new QTcpSocket();
//初始化各类成员变量
ConnectOrNot = true;
LoginOrNot = false;
LoginIP = "";
LoginName = "";
//绑定Tcp套接字收数据处理槽
connect(ClientSocket,SIGNAL(readyRead()),this,SLOT(RcvServerInfoSlot()));
//界面初始化
initUI();
}
3.1.2 界面初始化函数initUI
主要是完成主界面窗口中玩家列表树的初始化,比较简单,代码如下:
void ClientFrame::initUI()
{
//设置树控件表头隐藏,同时设置树节点图标大小
ui->PlayerTreeView->setHeaderHidden(true);
ui->PlayerTreeView->setIconSize(QSize(48,48));
//创建玩家列表树节点
PlayerTree = new QTreeWidgetItem;
PlayerTree->setText(0,"玩家列表");
ui->PlayerTreeView->addTopLevelItem(PlayerTree);
//创建房间列表树节点
RoomTree = new QTreeWidgetItem;
RoomTree->setText(0,"房间列表");
ui->PlayerTreeView->addTopLevelItem(RoomTree);
//设置玩家列表树为展开
ui->PlayerTreeView->expandAll();
}
3.2 ClientFrame成员变量定义
目前,一共定义以下几个成员变量,如下:
QTcpSocket *ClientSocket;//Tcp连接套接字
QString LoginName;//玩家登陆姓名
QString LoginIP;//玩家登陆IP
QString LoginIcon;//玩家登陆头像索引
bool LoginOrNot;//表明有没有登陆成功,默认false
bool ConnectOrNot;//服务连接成功标志,默认true
3.3 ClientFrame信号定义
目前,只自定义了一个信号,用于主界面ClientFrame在收到服务端送回的登陆注册响应信息后,将响应信息加工后通过该信号送还给SLoginAndRegistor登陆注册窗口,同时一些其他信息也通过该信号传给SLoginAndRegistor。我定义信号名称为:loginAndRegInfoReturn(QString Info)。
3.4 SLoginAndRegistor槽实现
3.4.1 void sendMsgToServerSlot(QString Msg)槽函数
这个槽函数,主要是用来响应从SLoginAndRegistor送来的注册或登陆信息,并依据给类信息,将信息通过Tcp套接字(程序中定义为ClientSocket)发送给服务器端。具体代码如下:
voidClientFrame::sendMsgToServerSlot(QString Msg)
{
QString IP; QString Port; QString Name; QString Password; QStringHeadIcon;
QString Temp;
bool flag=false;
//将登陆信息提取出来
QList <QString> StrList = Msg.split(":");
//先连接服务器,这里做了一个控制,主要是为了避免用户在登陆窗口登陆或注册不成功不再次连接服务器。这里对连接服务器的操作进行封装,形成了connectToServer函数,后面将给出
if(ConnectOrNot)//如果从来没连接过,则连接服务器
{
IP = StrList.value(1);
Port = StrList.value(2);
//根据登陆窗口送来的服务器IP、端口连接服务器
flag = connectToServer(IP,Port);
//连接成功,就将ConnectOrNot置为false,防止后续重复连接
if(flag)
ConnectOrNot = false;
else
return;//连接不成功就直接返回
}
//是注册信息
if(Msg.startsWith("REG"))
{
//取出信息
Name = StrList.value(3);
Password = StrList.value(4);
HeadIcon = StrList.value(5);
//向服务器发送注册信息
Temp = "REG:" + Name + ":" + Password +":" + HeadIcon;
//通过Tcp套接字将信息发送置服务器端
ClientSocket->write(Temp.toLocal8Bit());
ClientSocket->flush();
return;
}
//是登陆信息
if(Msg.startsWith("LOGIN"))
{
//取信息
Name = StrList.value(3);
Password = StrList.value(4);
//向服务器发送登陆信息
Temp = "LOGIN:" + Name + ":" + Password;
//将登陆信息发送至服务器端
ClientSocket->write(Temp.toLocal8Bit());
ClientSocket->flush();
return;
}
}
3.4.2 void RcvServerInfoSlot (QString Msg)槽函数
这个槽函数,主要用来响应从服务器端接收到信息后的处理槽,现在只做了登陆注册信息,那么在该槽内,就需要对服务器返回的注册、登陆响应信息进行处理,并把结果返回给登陆窗口SLoginAndRegistor。另外,如果用户成功登陆,服务器还要把登陆用户的信息发给所有已在线人员,同时用户下线时,服务器也要把用户下线信息发给每个在线用户,因此还需要完成上线、下线用户信息的分发处理。后续还有其他信息处理时,也在这里添加,代码如下:
void ClientFrame::RcvServerInfoSlot()
{
QByteArray Buffer;
QString Temp,NameTemp,IPTemp,str;
qint32 HeadIconTemp;
//读取缓冲区数据,依然是一行一行读取
Buffer = ClientSocket->readLine();
while(!Buffer.isEmpty())
{
Temp.clear();
//将数据转化为字符串,这里使用fromLocal8Bit方法是为了显示汉字
Temp = QString::fromLocal8Bit(Buffer);
QStringList StrList,TempList;
//送来的是服务器停止信息。在服务器端实现,我把服务器停止按钮响应也完成了,这里需要处理
if(Temp.startsWith("SERVERSTOP"))
{
//已经成功登陆了
if(LoginOrNot)
{
//显示提示信息框
QMessageBox::information(this,tr("提示"),"服务器正在重起,客户端将自动关闭,请重起稍后登陆",QMessageBox::Yes);
//关闭主界面窗口
this->close();
}
else
{
//因为是服务器端送来停止服务信息,因此此时的套接字连接就失效了,必须将ConnectOrNot置为true,确保用户再次进行登陆或注册动作,重新连接服务器
ConnectOrNot = true;
//因为没有成功登陆,因此此时的主界面ClientFrame是没有显示的,因此需要将信息通过loginAndRegInfoReturn信号传回给SloginAndRegistor登陆窗口
emit loginAndRegInfoReturn("服务器正在重起,请稍后登陆");
}
}
//服务器回馈的信息是登陆信息
if(Temp.startsWith("LOGIN"))
{
StrList.clear();
StrList = Temp.split(":");
//是登陆成功信息
if(StrList.value(1) == "LOGIN_OK")
{
//向登陆注册窗口发送登陆成功信息
emitloginAndRegInfoReturn("登陆成功!");
//将登陆成功与否标志置true,并将登陆名,登陆IP,头像索引取出
LoginOrNot = true;
LoginName = StrList.value(2);
LoginIP = StrList.value(3);
LoginIcon = StrList.value(4);
//将主界面窗口标题设置成与用户信息相关的标题
this->setWindowTitle("欢迎"+LoginName+"("+LoginIP+")进入够级奇妙之旅V1.0,赶快约小伙伴吧!");
//最大化显示主界面,注意只有登陆成功,才会显示主界面
this->showMaximized();
}
//用户名或密码错误
if(StrList.value(1) =="LOGIN_USERPWDERROR")
{
emitloginAndRegInfoReturn("用户名或密码错误!");
}
//该用户已经登陆
if(StrList.value(1) == "LOGIN_RELOGIN")
{
emit loginAndRegInfoReturn("该用户已登陆,请重新登陆!");
}
}
//服务器返回的是注册信息
if(Temp.startsWith("REG"))
{
StrList.clear();
StrList = Temp.split(":");
//注册成功
if(StrList.value(1) == "REG_OK")
{
emit loginAndRegInfoReturn("注册成功,请登陆!\n"+StrList.value(2));
}
//该用户名已被注册
if(StrList.value(1) == "REG_NAMEREGED")
{
emitloginAndRegInfoReturn("该用户名已被注册!");
}
//服务器故障
if(StrList.value(1) == "SERVERERROR")
{
emitloginAndRegInfoReturn("服务器未开启");
}
}
//用户成功登陆时,服务器除了发回成功登陆信息外,还要给所有在线用户发送刚上线用户的信息,这样各个客户端,可以通过该信息,将其更新到各自主界面窗口的玩家列表树中
if(Temp.startsWith("ONLINE"))
{
//如果刚上线的是自己,服务器发回的信息是所有在线玩家的信息,这个服务器端已完成
if(Temp.startsWith("ONLINEYOU"))
{
StrList =Temp.split("|");
//循环读取每个已在线玩家信息,并将其加到玩家列表树中
for(inti=1;i<StrList.size()-1;i++)
{
TempList =StrList.value(i).split(":");
//取出信息
NameTemp =TempList.value(0);
HeadIconTemp =TempList.value(1).toInt();
IPTemp = TempList.value(2);
//通过姓名和IP在玩家列表树中查找,没找到才添加
str = NameTemp +"\n" + IPTemp;
QList <QTreeWidgetItem*> PlayListTemp =
ui->PlayerTreeView->findItems(str,Qt::MatchExactly |Qt::MatchRecursive);
//看看有没有已经添加过该玩家,没添加才添加进玩家列表
if(PlayListTemp.size()==0)
{
QTreeWidgetItem *TempPlayer = new QTreeWidgetItem;
str.sprintf(":/HeadIcon/image/headIcon/%02d.png",HeadIconTemp+1);
TempPlayer->setIcon(0,QIcon(str));
str = NameTemp +"\n" + IPTemp;
TempPlayer->setText(0,str);
PlayerTree->addChild(TempPlayer);
}
}
}
//如果自己不是刚上线玩家,那么服务器送来的只是一个刚上线的玩家信息
else
{
StrList =Temp.split(":");
NameTemp = StrList.value(1);
HeadIconTemp =StrList.value(2).toInt();
IPTemp = StrList.value(3);
str = NameTemp + "\n"+ IPTemp;
//在玩家列表树中,查找是否已存在该玩家,如果没有就添加到树中
QList <QTreeWidgetItem *>PlayListTemp = ui->PlayerTreeView->findItems(str,Qt::MatchExactly |Qt::MatchRecursive);
if(PlayListTemp.size()==0)
{
QTreeWidgetItem *TempPlayer = new QTreeWidgetItem;
str.sprintf(":/HeadIcon/image/headIcon/%02d.png",HeadIconTemp+1);
TempPlayer->setIcon(0,QIcon(str));
str = NameTemp + "\n"+ IPTemp;
TempPlayer->setText(0,str);
PlayerTree->addChild(TempPlayer);
}
}
}
//处理服务器送来的某个玩家下线的消息
if(Temp.startsWith("OFFLINE"))
{
StrList.clear();
StrList = Temp.split(":");
//从玩家列表中中找到他,并删除,以用户名和IP为关键字查询
QList <QTreeWidgetItem *> PlayListTemp =ui->PlayerTreeView->findItems(StrList.value(1) +"\n" +StrList.value(2),Qt::MatchExactly | Qt::MatchRecursive);
//找到该玩家,则删除
if(PlayListTemp.size()!=0)
{
//删除树控件节点,要区分该节点有没有树节点
//如果没有父节点,则直接删除
if(PlayListTemp.at(0)->parent() == Q_NULLPTR)
{
ui->PlayerTreeView->setCurrentItem(PlayListTemp.at(0));
delete ui->PlayerTreeView->takeTopLevelItem
(ui->PlayerTreeView->currentIndex().row());
}
else //如果有父节点,则需要用父节点来takechild
{
//由于takechild的输入是该节点的行号,所以先将该节点设定为选中,这样就知道该节点的行号了
ui->PlayerTreeView->setCurrentItem(PlayListTemp.at(0));
deletePlayListTemp.at(0)->parent()->takeChild(ui->PlayerTreeView->currentIndex().row());
}
}
}
//读取下一行数据
Buffer = ClientSocket->readLine();
}
}
3.4.3 bool connectToServer(IP,Port)实现
刚才在实现sendMsgToServer槽时,用了connectToServer方法,比较简单,这里将贴代码,实现见注释:
bool ClientFrame::connectToServer(QStringIP,QString Port)
{
//无论有没有连接,先取消连接
ClientSocket->abort();
//连接至服务器
ClientSocket->connectToHost(IP, Port.toUInt());
//等待连接成功
if(!ClientSocket->waitForConnected(30000))
{
//连接不成功,向登陆窗口回复服务器未开启信息
emit loginAndRegInfoReturn("服务器未开启!");
return false;
}
return true;
}
4 服务器端注册功能响应实现
这里完成了客户端注册信息的功能,因此需要去对服务器端软件进行更新,提供注册信息的响应,主要修改服务器端SInfoThread类中answerClientDataSlot槽函数,再其中增加注册信息的响应,代码直接加响应登陆信息(LOGIN)之后即可,代码如下:
//注册信息处理
if(StrList.value(0) == "REG")
{
//取信息
Name = StrList.value(1);
Password = StrList.value(2);
Temp = StrList.value(3);
HeadIcon = Temp.toInt();
//调用数据库类进行注册
int ret = ServerConfig->ServerDataBase.regServer(Name,Password,HeadIcon);
switch (ret)
{
case 0:
//返回0,表示数据异常
Temp.clear();
Temp ="REG:SERVERERROR";
//通过SinfoThread的ThreadID组成键值,将该用户加入到用户Map中
str.sprintf("NotLogin%d",ThreadID);
if(!OnLineUser.contains(str))
OnLineUser.insert(str,this);
//向用户回馈服务故障的错误
AnswerClient->write(Temp.toLocal8Bit());
AnswerClient->flush();
break;
case 1:
//用户名已被注册
Temp.clear();
Temp ="REG:REG_NAMEREGED";
//以SInfoThread的ThreadID为键值,将其加入用户Map,OnLineUser
str.sprintf("NotLogin%d",ThreadID);
if(!OnLineUser.contains(str))
OnLineUser.insert(str,this);
//将用户名已被注册信息发回客户端
AnswerClient->write(Temp.toLocal8Bit());
AnswerClient->flush();
break;
case 2:
//注册成功
Temp.clear();
Temp ="REG:REG_OK:Name-"+Name+"-Password-"+Password;
//以SInfoThread的ThreadID为键值,将其加入用户Map,OnLineUser
str.sprintf("NotLogin%d",ThreadID);
if(!OnLineUser.contains(str))
OnLineUser.insert(str,this);
//将用户名成功注册信息发回客户端
AnswerClient->write(Temp.toLocal8Bit());
AnswerClient->flush();
break;
default:
break;
}
}
5 登陆注册功能演示
至此,服务器端以及客户端登陆注册功能已全部实现,试验了用户注册、登陆情况,下面就实际运行效果截图如下:
图三 登陆窗口登陆成功
图四 登陆窗口注册成功
图五 客户端主界面登陆后界面
本来,还有几张服务器端的图片的,由于显示上传图片的大小,下次再贴图把。