作者:小成Charles
原创作品
转载请标注原创文章地址:https://blog.csdn.net/weixin_42999453/article/details/112363393

一、引言
最近一直在联系网络通信的项目,一开始准备使用纯C/C++去写服务器的,虽说这样效率很高,但是写起来超级麻烦啊,第一个C/S项目,我就利用QtQnetwork模块去编写了,话说我中学的梦想就是能够自己做一个社交软件,最近开启了名为“轻聊”的项目,今年要考研,也不知道能写到多少…话不多说,上一下轻聊v1.0.1客户端的的截图;
(ps:界面很丑,只是实现了功能,下一步将继续完善功能和界面)

二、功能实现
目前实现了最基本的功能:

  1. 登录服务器
  2. 退出服务器
  3. 实时更新当前服务器在线的所有用户
  4. 双击用户列表可实现发送消息给指定用户

三、设计思路
我对服务器的设计思路毫无头绪,都是想到什么写什么,虽然功能都能实现,但是性能低,后期会继续完善优化。
项目实现分为客户端服务端,服务端在linux环境下运行,众所周知服务器是不带界面的。客户端在登录,退出,发送消息等等需要和服务器交互的数据统一是通过Json数据包传送,这样方便管理和提供接口等等。
服务端收到json数据包消息后会将json数据传送给sendServer.h,这个类是专门用来解析json数据包并将解析后的结果传送给相应的客户端,这里我就是创建了两个类,一个送来接收消息一个用来发送消息,这样做的目的是方便代码管理以及高性能做准备。
客户端就简单点,只要对接收到的消息进行处理分析就可,后面代码分析会讲解。
由于本项目是基于UDP协议的,也就是面向无连接的,这样收发消息很方便,但是这样的话对用户登录和下线的处理比较麻烦,在这里虽然也解决了,但是后期还是将会利用TCP协议进行登录连接,UDP进行收发消息,据说腾讯QQ就是这种模式。
这里我对我项目的大致逻辑画了一个图,方便大家理解!

四、客户端代码讲解
(1)登录和退出的代码块
登录和退出的实现就是点击绑定端口和解除绑定,这时候如果你点击绑定端口,你就会给服务器发送一个json数据包,其中键“way”对应的值就是“log”,相反对应的值就是“quit”,同时这里点击接触绑定的同时会把当前在线列表清空

void MainWindow::on_actBind_triggered()
{//开始绑定 就是上线quint16 port=ui->editPort->text().toUInt();QString     serverAddrEdit=ui->serverAddr->text(); //目标IPQHostAddress    serverAddr(serverAddrEdit);quint16     ServerPort=ui->ServerPort->text().toUInt();//目标portif (udpSocket->bind(port))//绑定端口成功{ui->chatPlainTextEdit->appendPlainText("**已成功绑定");ui->chatPlainTextEdit->appendPlainText("**绑定端口:"+QString::number(udpSocket->localPort()));QJsonDocument document=logOrQuit("log");QByteArray jsonArry=document.toJson(QJsonDocument::Compact);udpSocket->writeDatagram(jsonArry,serverAddr,ServerPort);ui->actBind->setEnabled(false);ui->actBort->setEnabled(true);}elseui->chatPlainTextEdit->appendPlainText("**绑定失败");
}void MainWindow::on_actBort_triggered()
{//解除绑定 就是下线QString     serverAddrEdit=ui->serverAddr->text(); //目标IPQHostAddress    serverAddr(serverAddrEdit);quint16     ServerPort=ui->ServerPort->text().toUInt();//目标portQJsonDocument document=logOrQuit("quit");QByteArray jsonArry=document.toJson(QJsonDocument::Compact);udpSocket->writeDatagram(jsonArry,serverAddr,ServerPort);ui->listWidget->clear();udpSocket->abort(); //不能解除绑定ui->actBind->setEnabled(true);ui->actBort->setEnabled(false);ui->chatPlainTextEdit->appendPlainText("**已解除绑定");
}

(2)发送消息模块
这里就是将输入的消息传送给sendMsg(QString msg)这个函数,这里将数据封装成json数据包,并且指定需要发送的客户端的地址,封装好后,发送给服务端。

void MainWindow::on_btnSend_clicked()
{//发送消息QString  msg=ui->msgEdit->text();//发送的消息内容sendMsg(msg);ui->chatPlainTextEdit->appendPlainText("[out] "+msg);ui->msgEdit->clear();ui->msgEdit->setFocus();
}
void MainWindow::sendMsg(QString msg)
{QString     serverAddrEdit=ui->serverAddr->text(); //目标IPQHostAddress    serverAddr(serverAddrEdit);quint16     serverPort=ui->ServerPort->text().toUInt();//目标portQJsonObject toObject;toObject.insert("addr",ui->targetAddrEdit->text());toObject.insert("port",ui->targetPortEdit->text());toObject.insert("msg",msg);QJsonObject msgObject;msgObject.insert("way","sendMsg");msgObject.insert("to",QJsonValue(toObject));QJsonDocument msgDocument;msgDocument.setObject(msgObject);QByteArray msgJsonArry=msgDocument.toJson(QJsonDocument::Compact);udpSocket->writeDatagram(msgJsonArry,serverAddr,serverPort); //发出数据报
}

(3)接收消息的模块
这里接收到消息之后就先把数据包发送给getWay()函数。这个函数将会判断”way“对应的值什么,然后执行相应的函数。

void MainWindow::onSocketReadyRead()
{//接受消息while(udpSocket->hasPendingDatagrams()){QByteArray   datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress    peerAddr;quint16 peerPort;udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);//分析json包QJsonParseError jsonError;QJsonDocument parseDocument=QJsonDocument::fromJson(datagram.data(),&jsonError);QJsonObject object=parseDocument.object();getWay(object);}
}
void MainWindow::getWay(QJsonObject object)
{QString way =  object.value("way").toString();if(way=="receiveMsg"){getMsg( object);}if(way=="updateUsers"){updateUsers(object);}
}

(4)更新用户在线列表
这里用到的空间是QlistWidge来存放用户数据列表
这里就是如果收到的数据的wayupdateUsers,将会执行如下函数功能,json数据中心传输过来的addrport两个数组一一对应,遍历并获得数据,创建QlistWidgetItem对象,设置用户地址和端口;我这里每次都会先清空所用列表再一一重新创建来更新列表,这样效率很差,不建议使用,后期会优化这一部分,因为客户端界面没有开始做就不讲究效率问题了。

void MainWindow::updateUsers(QJsonObject object)
{//接收json包分析数据 并实现更新在线用户列表   if(object.contains("way")){QJsonValue value=object.value("way");if(value.isString()){QString way=value.toString();qDebug()<<way;if(way=="updateUsers"){ui->listWidget->clear();                if(object.contains("addr")&&object.contains("port")){QJsonValue addrValue=object.value("addr");QJsonValue portValue=object.value("port");QJsonArray addrArry=addrValue.toArray();QJsonArray portArry=portValue.toArray();for (int i = 0; i < addrArry.size(); ++i) {QString addrStr=addrArry.at(i).toString();QString portStr=portArry.at(i).toString();onlineAddr.append(addrStr);onlinePort.append(portStr);QListWidgetItem *item=new QListWidgetItem();item->setData(i,i);item->setText("用户IP:"+addrStr+"用户端口:"+portStr);ui->listWidget->addItem(item);}}}}}
}

(5)最后就是将消息添加到聊天框

void MainWindow::getMsg(QJsonObject object)
{QJsonObject fromObject=object.value("from").toObject();QString fromAddr=fromObject.value("addr").toString();QString fromPort=fromObject.value("port").toString();QString fromMsg=fromObject.value("msg").toString();QString peer="[From "+fromAddr+":"+fromPort+"] ";ui->chatPlainTextEdit->appendPlainText(peer+fromMsg);
}

五、服务端代码讲解
(1)接收数据模块
这里就是接收到消息之后创建一个SendServer类的对象,这个类主要做数据处理和转发数据的,创建了两个connect函数,分别监听如果有用户登录和用户退出的事件,然后将数据传出来进行更新在线用户列表,记住这里最后一定要将这个对象delete掉,不然最后可能会导致内存泄漏等问题!

void udpserver::onReadyRead()
{//server to reveciveQByteArray   datagram;datagram.resize(udpServer->pendingDatagramSize());QHostAddress    peerAddr;quint16 peerPort;udpServer->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);qDebug()<<datagram.data();SendServer *send=new SendServer ();connect(send,SIGNAL(newUsers(QStringList)),this,SLOT(inserNewUsers(QStringList)));//connect the signals before the emitconnect(send,SIGNAL(quitUsers(QStringList)),this,SLOT(pushQuitUsers(QStringList)));send->getReceiveData(datagram.data(),datagram.size(),peerAddr,peerPort);delete send;//free object}

(2)更新用户列表模块
这里有两个函数,分别是inserNewUserspushQuitUsers,代码逻辑大相径庭,就是对从SendServer类中监听到的数据进行判断操作的槽函数,这里也创建了一个SendServer对象把onlineUserList(存放在线用户的容器)传送给了这个类中的函数sendAllCilentsTheUsers(onlineUserList);,它将实现把数据发送给所用客户端

void udpserver::inserNewUsers(QStringList logUserInfo)
{//update the userListfor (int i=0;i<onlineUserList.length();++i) {if(onlineUserList.at(i)==logUserInfo){qDebug()<<"the same log users";SendServer *updateSend=new SendServer ();updateSend->sendAllCilentsTheUsers(onlineUserList);delete updateSend;return;}}onlineUserList.append(logUserInfo);SendServer *updateSend=new SendServer ();updateSend->sendAllCilentsTheUsers(onlineUserList);delete updateSend;qDebug()<<onlineUserList;}
void udpserver::pushQuitUsers(QStringList quitUserInfo)
{for (int i=0;i<onlineUserList.length();++i) {if(onlineUserList.at(i)==quitUserInfo){qDebug()<<"it can be deleted";onlineUserList.removeAt(i);qDebug()<<"removved:"<<onlineUserList;SendServer *updateSend=new SendServer ();updateSend->sendAllCilentsTheUsers(onlineUserList);delete updateSend;return;}else {qDebug()<<"reQuited!";}}
}

(3)最后贴出SendServer.h所用的代码块
感兴趣可以自己看一下,这里getWay函数实现分析way,然后执行对应的函数块,sendLOgUsersendQuitUser分别触发了newUsersquitUsers这两个信号,并将数据传送出去,对应上面讲的那两个connect函数,然后又去执行sendAllCilentsTheUsers实现实时更新用户列表,如果是执行sendMsgToClients这个函数,就会发送者ip和端口以及消息打包成json发送给指定的客户端。

 SendServer::SendServer(QObject *parent) : QObject(parent)
{udpSendServer =new QUdpSocket ();}void SendServer::getWay( QJsonDocument parseDocument)
{QJsonObject object=parseDocument.object();if(object.contains("way")){QJsonValue value=object.value("way");if(value.isString()){QString way=value.toString();qDebug()<<way;if(way=="log"){sendLOgUser(object);}if(way=="sendMsg"){sendMsgToClients(object);}if(way=="quit"){sendQuitUser(object);}}}
}
void SendServer::getReceiveData(QByteArray data,int size,QHostAddress &peerAddr,quint16 &peerPort)
{QJsonParseError jsonError;QJsonDocument parseDocument=QJsonDocument::fromJson(data,&jsonError);if(!parseDocument.isNull()){temtLOgAddr =peerAddr;temtLogPort = peerPort;getWay(parseDocument);}
}
void SendServer::sendLOgUser(QJsonObject object)
{//send the log user to clientslogUserInfo.append(temtLOgAddr.toString());logUserInfo.append(QString::number(temtLogPort));qDebug()<<logUserInfo;emit newUsers(logUserInfo);
udpSendServer>writeDatagram(jsonArry,temtLOgAddr,temtLogPort);
}void SendServer::sendQuitUser(QJsonObject object)
{//send Quit usersquitUserInfo.append(temtLOgAddr.toString());quitUserInfo.append(QString::number(temtLogPort));qDebug()<<quitUserInfo;emit quitUsers(quitUserInfo);}void SendServer::sendAllCilentsTheUsers(QList <QStringList> onlineUserList)
{bool ok;QJsonArray addrArry;QJsonArray portArry;for (int i = 0;i<onlineUserList.length();i++) {QString addr=onlineUserList.at(i).at(0);QString port=onlineUserList.at(i).at(1);//        qDebug()<<"enter 1";addrArry.append(addr);portArry.append(port);}QJsonObject jsonObject;jsonObject.insert("way","updateUsers");jsonObject.insert("addr",QJsonValue(addrArry) );jsonObject.insert("port",QJsonValue(portArry));QJsonDocument document;document.setObject(jsonObject);QByteArray jsonArry=document.toJson(QJsonDocument::Compact);//forward the json to all clientsfor (int j = 0;j<onlineUserList.length();j++) {//        qDebug()<<"enter 2";QHostAddress addr(onlineUserList.at(j).at(0));quint16 port= onlineUserList.at(j).at(1).toUInt();qDebug()<<addr<<port;udpSendServer->writeDatagram(jsonArry,addr,port);}
}void SendServer::sendMsgToClients(QJsonObject object)
{QJsonObject toObject=object.value("to").toObject();QString toAddr=toObject.value("addr").toString();QString toPort=toObject.value("port").toString();QString msg=toObject.value("msg").toString();QHostAddress targetAddr(toAddr);quint16 targetPort=toPort.toUInt();QJsonObject fromObject;fromObject.insert("addr",temtLOgAddr.toString());fromObject.insert("port",QString::number( temtLogPort));fromObject.insert("msg",msg);QJsonObject sendObject;sendObject.insert("way","receiveMsg");sendObject.insert("from",QJsonValue(fromObject));QJsonDocument sendDoucment;sendDoucment.setObject(sendObject);QByteArray sendJsonArry=sendDoucment.toJson(QJsonDocument::Compact);udpSendServer->writeDatagram(sendJsonArry,targetAddr,targetPort);
}

五、总结
没有总结,接下来继续更新完善更能,继续更新博客,立个flag轻聊5.0将达到商用级别!

作者:小成Charles
原创作品
转载请标注原创文章地址:https://blog.csdn.net/weixin_42999453/article/details/112363393

小成开发日记---利用Qt/C++实现基于Udp协议的网络聊天室(分服务端和客户端的开发【轻聊v1.0.1】)相关推荐

  1. 简单服务端和客户端的开发

    在编程之前我安装了一些软件工具包,其中包括vc2005.gsoap.Flash builder 4.5等等,我利用vc2005开发服务端,FB来开发客户端,当然我对于这些工具以及环境还不是很熟悉,也是 ...

  2. 【Vaadin教程】利用IDEA开发基于Vaadin网络聊天室程序

    为什么80%的码农都做不了架构师?>>>    利用Vaadin,我们可以轻松的开发出丰富的web界面,像写Swing一样写GUI,感觉什么extjs之类的弱爆了!下面是我做的一个网 ...

  3. 盘点直播直播平台软件开发技术中的编解码、直播协议、网络传输与简单实现

    盘点直播直播平台软件开发技术中的编解码.直播协议.网络传输与简单实现 编解码 视频封装格式就是我们通常所说的 .mp4,.flv,.ogv,.webm 等,它其实就是一个盒子,用来将实际的视频流以一定 ...

  4. 仿微信的网络聊天室项目开发【完整源码讲解】

    目录 总体开发思路 服务器端 服务器界面设计 建立TCP服务器端通信 建立服务器消息发送输出流 建立服务器消息接收输入流 建立服务器实时消息通信线程 设置服务器通信自由断开 客户端 客户端界面设计 建 ...

  5. 《 Python程序设计项目案例》— 用Python开发的基于TCP通讯协议的私人聊天室 (期末大作业、结课作业、课程设计、毕业设计)

    基于Python与TCP协议的私人聊天室(GUI交互界面,用户注册.用户登录.实时聊天,文件上传与下载) 用Python开发的基于TCP通讯协议的实时聊天通讯和文件共享应用 目录 基于Python与T ...

  6. Java服务端人脸识别实战开发优化

    最近有合作公司的项目需要服务端人脸识别的开发,于是就用了公司的人脸识别SDK开发,由于之前对服务端开发介绍的资料比较少,正好这次又做了这个项目,花了几天的开发,这里就简单分享一下个人的见解. 影响性能 ...

  7. Java中利用socket实现简单的服务端与客户端的通信(入门级)

    Java编程中,要想要使用网络通信,就离不开Socket编程,在此对socket进行简单的介绍.首先声明,这是一个入门级的介绍,仅仅简单的实现了客户端向服务端发送数据,服务端正常的接收数据,当接收到特 ...

  8. axis idea 设置apache_利用IDEA创建Web Service服务端和客户端的详细过程

    创建服务端 一.file–>new–>project 二.点击next后输入服务端名,点击finish,生成目录如下 三.在 HelloWorld.Java 文件中右击,选 WebServ ...

  9. wss协议 c 服务器,利用LIBWEBSOCKETS写WS、WSS服务端和客户端

    libwebsockets是一款轻量级用来开发服务器和客户端的C库.按照官方(https://libwebsockets.org/)给出的介绍来看,它不仅支持ws,wss还同时支持http与https ...

最新文章

  1. Linux (Ubuntu)使用vi和vim方向键变成了ABCD
  2. 使用Docker-数据卷挂载案例1
  3. mysql strcmp s1 s2_mysql常用函数
  4. flo file_Flo菜单简介:可扩展的拇指友好型移动导航
  5. make: *** 没有规则可制作目标“distclean”。 停止。_Makefile伪目标
  6. java 昵称1到32位字符_Java期末考试编程题复习
  7. 对Chrome自动发送邮件插件的改进
  8. 【阅读笔记】:End-to-end Structure-Aware Convolutional Networks for Knowledge Base Completion
  9. 燃爆了!胡歌秒变最帅产品经理发布荣耀V20!
  10. Linux 学习笔记 (四)Ubuntu14.04 解决上网问题安装无线网卡驱动
  11. python编程方式_python的两种编程方式是什么
  12. 接口测试通用测试用例
  13. Vue打包优化篇-CDN加速
  14. java 判断手机运营商_JS正则表达式判断手机号所属运营商
  15. 促销 Eventide Clockworks 经典传奇效果器插件合集
  16. 影刀学习抓取网页详情
  17. Air test ios类使用
  18. 5g宣传方案_活动创意策划方案要向“5G时代”看齐
  19. MFC中CString转为char*;char*转为CString
  20. 服务器加固安全指导书

热门文章

  1. 将u盘插入apple苹果系统后发现再插入window系统发现无法新建文件夹或者文件,新建new功能消失了
  2. ## YARN运行资源配置
  3. Java17线程进程
  4. 搭建个人博客(论坛)的建议
  5. python打印菱形,n可随意定义
  6. Neuroimaging 海外职位信息
  7. 揭秘考研英语阅读理解技巧(何凯文)
  8. java线程-从生产者和消费者模型说起
  9. 我们年轻时,为什么要辛苦赚钱,这是我听过的最好回答!
  10. Cascade-RCNN