基于QT的网络聊天系统
基于QT的⽹络聊天系统
1.项⽬概述
这个项⽬是来的源代码,我把源代码⼀⾏⼀⾏的⾃⼰敲进去再运⾏调试出来的,实际上是学习,不是我的什么开发。该项⽬有两个部分,⼀个是客户端,⼀个是服务器端。客户端负责监听客户端发来的信息并做出相应的处理,发送⼴播。客户端实现注册、登录、对话、接受⼴播等功能
在客户端中,没有多进程,只有⼀个进程负责对所有⽤户的处理,所以服务器⼴播的发送就要求所有客户端绑定⼀个端⼝。客户端中的好友列表⾥实际上是服务器数据库⾥所有的 ⽤户,这也就是说这个项⽬实际上是个简单的聊天室程序,⽽且没有聊,只能进⾏点对点的聊天,还没有⽂件传送这样的功能。
这⾥刨个坑,以后有时间有能⾥的时候会弥补上⾯的缺陷,做成⼀个完整的聊天室程序。具体就是:服务器改为多进程服务器,可以为每个客户开辟⼀个进程;客户端中实现聊、⽂件传输功能。
2.运⾏环境与项⽬部署
QT4.7,以及QT -Embedded移植,安装sqlite数据库,详见我的其他博客。
3.使⽤说明和界⾯介绍
服务器端如下:
左上部分是对数据库的显⽰,这⾥只显⽰了ID、昵称、在线状态。
IP地址填写你的服务器主机的IP ,端⼝绑定8888,点击“开始监听”以后,就开始监听。发送按钮就可以把“hello client”发送出去。
客户端界⾯如下:
在登录和注册之前必须要设置服务器的IP地址和端⼝(和上⾯必修⼀致),在程序中已经写好,点击设置就可以,
点击确定后
才可注册和登录。
点击注册新帐号后弹出注册界⾯
填好上诉信息后确定即完成注册,然后在上⾯的界⾯中登录,登录后弹出绘画界⾯
下⾯是接受服务器的⼴播,上⾯是在线⽤户列表,点击其中⼀个弹出对话界⾯,
"ssss"是⾃⼰发送的内容,有下⾯的⽂本框输⼊,点击“发送”后显⽰在上⾯,对⽅的回话也现在在此。
在具体设计和实现之前,⾸先列出内部通信的信息。客户端和服务器端的功能实现就是依靠接受不同的信息,判别后才完成相应的功能
Tcp消息服务器端客户端
(1):  “MSG_CLIENT_USER_REGISTER”接收
并读取⼀起发来的ID,PWD,Name,插⼊数据库,若已有则发送(2),插
⼊成功(注册成功)则发送(3)
发送
从注册界⾯获取的数据验证后发
送,⼀起发送的还有
古典音乐有哪些
ID,PWD,Name。
(2):"MSG_ID_ALREADY_EXIST"发送
新⽤户数据插⼊数据库操作时发现已有该⽤户,发送此信息
接收
接收后弹出Qessage对话框,提⽰
已经注册,不要重复注册
(3):"MSG_CLIENT_REGISTER_SUCCESS"发送接受
接受后将此消息通过udp发送给客户端
(4):"MSG_USER_LOGIN"接收
同时接收的还有ID和PWD。服务器⾸先查询该ID是否存在,不存在
则发送(5),密码不对则发送(6),已经登录则发送(7),最后发送(8)
发送
由登录界⾯的确定按钮发送,同时
发送的还有ID和PWD。
(5):"MSG_ID_NOEXIST"发送
电影叶落归根服务器查询该ID不存在
接收
接收后弹出Qessage对话框,提⽰
该ID不存在
(6):"MSG_PWD_ERROR"发送
服务器查询密码不匹配
接收
接收后弹出Qessage对话框,提⽰
密码不匹配
(7):"MSG_LOGIN_ALREADY"发送
服务器查询该⽤户已登录
接收
接收后弹出Qessage对话框,提⽰
该⽤户已登录
(8):"MSG_LOGIN_SUCCESS"发送
发送并在数据库中修改状态为登录状态,并存储本次登录的IP
接收
接收后弹出好友界⾯准备聊天
Udp消息服务器端客户端
(1)."MSG_CLIENT_NEW_CONN"接收
同时接受的还有本次登录的ID,此后向发送本次消息的客
户端回写(5)、向所有客户端发送(7)
发送
会话界⾯初始化时发送,同时发送的还有该次登
录的ID
(2)."MSG_CLIENT_REGISTER_SUCCESS"接收
抖音怎么赚钱
接受后刷新显⽰数据库的tableview
发送
在TCP接受(3)后发送
(3)."MSG_USER_LOGOUT"接收
⾸先在数据库中设置该⽤户下线,刷新tableview,随后向
所有⽤户发送(8)
发送
会话界⾯按下退出按钮后发送,⼀起发送的还有
该次登录的ID
(4)."MSG_CLIENT_CHAT"接收
接收后向对话⽅的客户端发送(4),⼀起发送的还有发起
对话的客户ID以及谈话内容
发送
⼀起发送的还有⾃⼰的ID(发起会话的ID),对
话⽅的ID以及谈话内容
接收
接收后调⽤回话界⾯,显⽰发来的内容和发起⼈
的ID
(5)."MSG_ALL_USER_ONLINE"接收
同时发送的还有所有在线⽤户的ID和NAME
接收
接受到的还有所有在线⽤户的ID和NAME,⽤于
显⽰⾃⼰的好友列表。
(6)."MSG_SERVER_INFO"发送
向所有⽤户发送系统消息,由服务器端发送按钮发送
接收
显⽰系统消息
(7)."MSG_NEW_USER_LOGIN"发送
向所有⽤户发送新登录⽤户的ID和NAME
一寸照片的尺寸
接收
在好友列表中添加这名刚登录的⽤户
(8)."MSG_CLIENT_LOGOUT"发送
在接收(3)后向所有客户端发送,⼀起发送的还有下线⽤
户的ID和name(显⽰在各个⽤户列表的),
接收
⼀起接收的还有下线⽤户的ID和NAME,接受后
在⾃⼰的显⽰列表中删除这名⽤户
5.部分重要功能语句
很多核⼼的设计和代码编写都是重复的,了解这些代码将有益于程序的理解        (1).Tcp和Udp消息发送
QString msgType="MSG_ID_ALREADY_EXIST";
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);          //拿QByteArray对象来进⾏加⼯也就是所谓的串⾏化
out.setVersion(QDataStream::Qt_4_6);        //设置数据流的版本,客户端和服务器端使⽤的版本要相同
out<<(quint16)0<<msgType;                          //这⾥其实要分成两部分看前⾯的0只是⽤来占空间的,后⾯才是真正要发送的数据
out.device()->seek(0);                                      //吧流定位到开始的位置,这个位置是刚才⽤数字0来进⾏填充的空间
out<<(quint16)(block.size()-sizeof(quint16));          这⾥是计算出真正要发送的数据⼤⼩,把这个计算后的值填⼊之前提前占好的空间中
使⽤out<<(quint16) 0,在block的开始添加了⼀个quint16⼤⼩的空间,也就是两字节的空间,它⽤于后⾯放置⽂件的⼤⼩信息。然后out<<msgType输⼊实际的⽂件(当然msgType后⾯也跟追加内容,⽐如ID,PWD,NAME),这⾥是字符
串"MSG_ID_ALREADY_EXIST"。当⽂件输⼊完成后我们在使⽤out.device()->seek(0);返回到block的开始,加⼊实际的⽂件⼤⼩信息,也就是后⾯的代码,它是实际⽂件的⼤⼩:out<<(quint16) (block.size() – sizeof(quint16))。这样block.size包括两部分:⽂件⼤⼩信息(2字节)+⽂件实际⼤⼩(msgType的长度)。
我们都在数据流的最开始写⼊完整⽂件的⼤⼩信息,这样接收端就可以根据⼤⼩信息来判断是否接受到了完整的⽂件。
尤其是在使⽤Tcp时:TCP数据是⼀串长长的流,你事先不知道它的长度,因此你需要现⽤⼀个东西来占⽤TCP流最开始的那段空间,当加⼊真正要发送的数据的时候,流的⼤⼩才能确定下来,这个时候就吧计算好的结果放到之前占的那个空间去
(2)static和const修饰类的成员函数
留学中介机构
static修饰静态成员函数,const修饰的成员函数的this指针所指向的对象是⼀个常量。
静态函数只能使⽤本类中的静态成员数据或函数,不能使⽤⾮静态成员;const修饰的成员函数不能调⽤、修改对象的数据成员,在函数体内只能调⽤const修饰的成员函数,
static和const所修饰的成员函数在类呗引⽤时会⾃动运⾏。
使⽤QSqlQueryModel和QTableView可以显⽰数据库,代码⾥,数据库⾥有⼀列是表⽰是否在线(⽤01存储),1表⽰在线,0表⽰离线,但是显⽰的时候不希望显⽰“1”或者“0”,⽽是希望显⽰汉字“在线”、“离线”。
码⾥⾸先写了⼀个class MySqlQueryModel : public QSqlQueryModel,然后只有⼀个共有成员:
QVariant data(const QModelIndex &item,int role=Qt::DisplayPropertyRole) const;
其内容如下:
QVariant value=QSqlQueryModel::data(index,role);
if(value.isValid() && role==Qt::DisplayRole && lumn()==2)
{
value=(Int()==1?tr("在线"):tr("离线"));
return value;
正月}
然后在引⽤QSqlQueryModel时,数据库⾥的0和1会⾃动转换成“在线”、“离线”。
(3)正则表达式
⽤户在界⾯输⼊的数据必须进⾏检验,检验合格后才能使⽤。⽐如ID号要求5~9位数字,IP号要求型如x等等。正则表达式准确书写⽐较繁琐,这⾥列出⽤的。
QRegExp rx("^[1-9]{1,2}[0-9]{4,7}$");                //5-9位ID号,第⼀位不能为0
QRegExp rxIp("\\d+\\.\\d+\\.\\d+\\.\\d+");              //IP地址
QRegExp rxPort(("[1-9]\\d{3,4}"));                      //端⼝号
rx.setPatternSyntax(QRegExp::RegExp);
if(!rx.exactMatch(id))
{
QMessageBox::warning(NULL,tr("提⽰"),tr("请输⼊5~9位数的QQ号"));
}
6.⼀些问题
在研究和调试代码的过程中遇到很多问题,有的很有价值,这⾥列出来。
(1).在⼀台机器中为什么不能登录两个客户端?
单进程的服务器⼴播发送要求所有客户端绑定⼀个公告已知的端⼝,程序中是“6666”,所以⼀台机器中不能有两个客户端运⾏。解决这个问题应该是设计多进程服务器,为每个客户端保留其端⼝号。
(2).Tcp和Udp消息的功能有何不同?
Tcp主要⽤户登录,Udp⽤于通信。
(3).chatFrrm Hash
这是⼀个类似与快捷⽅式的东西,把某个类和某个字符串(也可以是其他的)绑定。在聊天中,⽤户点击好友列表中的好友,相应会弹出和谁对话的对话框,⽐如,字符'a'就绑定了和'a'⽤户的聊天界⾯(类)
7.代码上传

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。