聊天室:
知识点:
static int l=50;//放在数据区 直到程序完全结束才会被释放,然而一般的数据都会存放在栈区。
如果是定义一个静态变量名的话,通常都会同时赋值,而且只会被赋值一次,并且永久存在。如果不手动赋值则变量默认为0;如果是定义一个静态对象名的话,那个对象名只会被创建一次,避免重复执行造成不必要的麻烦。
nextPendingConnection与sender的区别?
前者只可以使用一次,一般在第一次使用,如果在程序后期还需要使用则需要用sender
两者的作用都是接收socket数据。
Json数据类型的样例:{"type":"allinfo","from":"rym","date":"2048-9-5 10:40","info":"i am rym"}
emit 信号
手动发送信号(SIGNAL)用来激发相对应的槽函数(SLOT)。
例如下面的connect函数中的参数的意义connect(发送信号的对象,发送的信号,所触发槽函数的对象,所出发的槽函数)。
客户端构造函数片段:
this->connect(this->socket,SIGNAL(readyRead()),this,SLOT(readyReadSlot()));//准备好接受数据
this->connect(this->socket,SIGNAL(connected()),this,SLOT(connectedSlot()));//提示连接成功
this->disconnect(this->socket,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));
1.
this->connect(this->socket,SIGNAL(readyRead()),this,SLOT(readyReadSlot()));
在这行代码中设置的信号是readyRead()准备读信号,一旦有readyRead()信号产生,就会激发后面的readyReadSlot()槽函数,
void ChatClient::readyReadSlot()//接收来自服务器的数据
{
QByteArray data=this->socket->readAll();
handleData(data);
}
显而易见,这个槽函数的功能就是读取来自通过socket传输的来自服务器的数据。在这个函数里就引入了handleData()函数:
void ChatClient::handleData(const QByteArray &data)
{
QJsonObject obj=QJsonDocument::fromJson(data).object();//解析Json型的数据
QString type =obj.value("type").toString();//获取类型
QString date=obj.value("date").toString();//获取时间
QString info=obj.value("info").toString();//获取信息
QString fromName = obj.value("from").toString();//获取发送者
ui->onLineListWidget->clear();//在打印列表前应先清屏,因为客户的每次连接或者断开连接都会在服务器端触发槽函数向客户端发 //送数据,回导致重复冗余的信息
//打印在线列表
QStringList list;
QString str=tr("%1").arg(obj.value("clientListInfo").toString());
qDebug()<<"str:"<<str;
list<<str;
ui->onLineListWidget->addItems(list);
qDebug()<<"efrwer ";
//以下是客户端之间互相发送的消息
if(type=="allinfo")
{
QString str=tr("%1 %2 send to all:\n%3").arg(fromName).arg(date).arg(info);//显示 arg()函数 tr()获取字符
ui->displayTextBrowser->append(str);//显示在界面
}
else if(type=="oneinfo")
{
QString str=tr("%1 %2 send:\n%3").arg(fromName).arg(date).arg(info);
ui->displayTextBrowser->append(str);
}
}
2.
this->connect(this->socket,SIGNAL(connected()),this,SLOT(connectedSlot()));//提示连接成功
同上,这行代码的作用是如果客户端成功连接上服务器以后就会发出一个connected()信号并且触发connectedSlot()槽函数:
void ChatClient::connectedSlot()//提示连接成功
{
QMessageBox::information(this,"提示","连接成功");
ui->ipLineEdit->setEnabled(false);
ui->portLineEdit->setEnabled(false);//设置不编辑
ui->fromNameLineEdit->setEnabled(false);
ui->connectBtn->setEnabled(false);
QJsonObject obj;
obj.insert("type","logininfo");
obj.insert("from",ui->fromNameLineEdit->text());
this->socket->write(QJsonDocument(obj).toJson());//往服务器写回数据,而且一旦写回数据后就没后面什么事了
}
3.
this->disconnect(this->socket,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));
好像没什么用,在UI设计的界面中分别由connect和disconnect两个按钮,这两个按钮还分别有事件处理程序:
//connect按钮的处理程序代码
void ChatClient::on_connectBtn_clicked()
{
if(ui->fromNameLineEdit->text()=="")
{
QMessageBox::warning(this,"Warnning","请注意输入的用户名不能为空!");//弹出的框
return;
}
this->socket->connectToHost(ui->ipLineEdit->text(),ui->portLineEdit->text().toInt());
}
//disconnect按钮的处理程序
void ChatClient::on_disconnectBtn_clicked()
{
this->socket->close();//关闭socket连接
//本来应该在disconnectedSlot中的代码,在那个程序里调试不出来 不明白其原因
ui->onLineListWidget->currentRow();
QMessageBox::information(this,"提示","断开连接");
ui->onLineListWidget->clear();//断开连接后将列表部分清屏
ui->ipLineEdit->setEnabled(true);
ui->portLineEdit->setEnabled(true);//设置可编辑
ui->fromNameLineEdit->setEnabled(true);
ui->connectBtn->setEnabled(true);
}
//向指定的人发送消息
void ChatClient::on_sendToOneBtn_clicked()
{
QString date=QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
QJsonObject obj;
obj.insert("type","oneinfo");
obj.insert("from",ui->fromNameLineEdit->text());
obj.insert("to",ui->toNameLineEdit->text());
obj.insert("date",date);
obj.insert("info",ui->sendToOneLineEdit->text());
this->socket->write(QJsonDocument(obj).toJson());
}
//群发消息
void ChatClient::on_sendToAllBtn_clicked()
{
QString date=QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
QJsonObject obj;
obj.insert("type","allinfo");
obj.insert("from",ui->fromNameLineEdit->text());
obj.insert("date",date);
obj.insert("info",ui->sendMessageToAlllineEdit->text());
this->socket->write(QJsonDocument(obj).toJson());//强制转换
}
//client 客户端 UI界面:
服务器:
构造函数代码片段:
this->clientListsocket=new QTcpSocket;
this->server=new QTcpServer;
this->server->listen(QHostAddress::Any,10086);//双冒号是枚举变量,设置端口号为10086并且任何ip地址均能连接
if(this->server->isListening())
qDebug()<<"The Chat Server is Listening...";
this->connect(this->server,SIGNAL(newConnection()),this,SLOT(newConnectionSlot()));
this->connect(this,SIGNAL(clientListChanged()),this,SLOT(clientListChangedSlot()));//this就是当前类的对象
1.
this->connect(this->server,SIGNAL(newConnection()),this,SLOT(newConnectionSlot()));
void CharServer::newConnectionSlot()
{
QTcpSocket *socket=this->server->nextPendingConnection();//发出槽函数
connect(socket,SIGNAL(readyRead()),this,SLOT(readyReadSlot()));//创建连接
connect(socket,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));//断开连接
ClientInfo* clientInfo=new ClientInfo;
clientInfo->ClientName = "";
clientInfo->clientSocket=socket;
clientList.append(clientInfo);//附加
emit clientListChanged();//手动发射信号
}
//对应的槽函数定义如下
void CharServer::readyReadSlot()//从客户端获取数据
{
QTcpSocket *socket=(QTcpSocket*)sender();
QByteArray data =socket->readAll();
QJsonObject obj=QJsonDocument::fromJson(data).object();
QString type=obj.value("type").toString();
if(type=="logininfo")
handleLogInfo(data);
else if(type=="allinfo")
handleAllInfo(data);
else if(type=="oneinfo")
handleOneInfo(data);
else
return;
}
void CharServer::handleLogInfo(const QByteArray &data)//提交登陆数据
{
QJsonObject obj=QJsonDocument::fromJson(data).object();//解析Json类型的数据
QString fromName=obj.value("from").toString();
QTcpSocket *s=(QTcpSocket*)sender();
//新添加
for(int i=0;i<clientList.length();i++)
{
if(s==clientList.at(i)->clientSocket)
{
clientList.at(i)->ClientName=fromName;
emit clientListChanged(); //发射信号
}
}
}
void CharServer::handleAllInfo(const QByteArray &data)//群发消息
{
for(int i=0;i<clientList.length();i++)
{
clientList.at(i)->clientSocket->write(data);//向客户端写数据
}
}
void CharServer::handleOneInfo( const QByteArray &data)//私聊消息的转发
{
QJsonObject obj=QJsonDocument::fromJson(data).object();
QString toname=obj.value("to").toString();
for(int i=0;i<clientList.length();i++)
{
if(toname==clientList.at(i)->ClientName)
{
clientList.at(i)->clientSocket->write(data);//向客户端写数据
}
}
}
void CharServer::disconnectedSlot()//断开连接
{
QTcpSocket *s=(QTcpSocket*)sender();
for(int i=0;i<clientList.length();i++)
{
if(s==clientList.at(i)->clientSocket)
{
delete clientList.at(i);
clientList.removeAt(i);
emit clientListChanged();//手动发送信号
}
}
}
2.
this->connect(this,SIGNAL(clientListChanged()),this,SLOT(clientListChangedSlot()));//this就是当前类的对象
这个clientListChangedSlot()槽函数的作用是一旦连接府服务器的客户有变化(客户登陆、连接或者断开服务器)就会被触发,通过上面的代码来看,触发该槽函数的方式为手动触发 emit clientListChanged();
void CharServer::clientListChangedSlot()//输出客户端的一些信息,有几个客户端 连上的客户端
{
QString date=QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss]");
qDebug()<<date;
qDebug()<<"clientList's length:"<<clientList.length();//客户列表长度
for(int i=0;i<clientList.length();i++)
{
qDebug()<<"client name"<<i<<":"<<clientList.at(i)->ClientName;//输出客户名称
qDebug()<<"client socket"<<i<<":"<<clientList.at(i)->clientSocket;//输出socket编号
}
//下面的代码是要在线的客户以QString类型存到clientListInfo中并且转换为Json格式给每位用户都发送一遍
QJsonObject obj1;
QString clientListInfo;
clientListInfo.append("");
qDebug()<<"clientListInfo:"<<clientListInfo;
for(int j=0;j<clientList.length();j++)
{
QString str=tr("%1 %2\n").arg(j+1).arg(clientList.at(j)->ClientName);
clientListInfo.append(str);
}
obj1.insert("clientListInfo",clientListInfo);
for(int i=0;i<clientList.length();i++)
clientList.at(i)->clientSocket->write(QJsonDocument(obj1).toJson());
}