基于QT的网络聊天系统

基于QT的网络聊天系统1.项目概述         这个项目是找来的源代码,我把源代码一行一行的自己敲进去再运行调试出来的,实际上是学习,不是我的什么开发。该项目有两个部分,一个是客户端,一个是服务器端。客户端负责监听客户端发来的信息并做出相应的处理,发送广播。客户端实现注册、登录、对话、接受广播等功能         在客户端中,没有多进程,只有一个进程负责对所有用户的处理,所以服务器广播的发送就要求所有

      1.项目概述

          这个项目是找来的源代码,我把源代码一行一行的自己敲进去再运行调试出来的,实际上是学习,不是我的什么开发。该项目有两个部分,一个是客户端,一个是服务器端。客户端负责监听客户端发来的信息并做出相应的处理,发送广播。客户端实现注册、登录、对话、接受广播等功能

          在客户端中,没有多进程,只有一个进程负责对所有用户的处理,所以服务器广播的发送就要求所有客户端绑定一个端口。客户端中的好友列表里实际上是服务器数据库里所有的 用户,这也就是说这个项目实际上是个简单的聊天室程序,而且没有群聊,只能进行点对点的聊天,还没有文件传送这样的功能。

           这里刨个坑,以后有时间有能里的时候会弥补上面的缺陷,做成一个完整的聊天室程序。具体就是:服务器改为多进程服务器,可以为每个客户开辟一个进程;客户端中实现群聊、文件传输功能。

      2.运行环境与项目部署

           QT4.7,以及QT -Embedded移植,安装sqlite数据库,详见我的其他博客。

      3.使用说明和界面介绍

      服务器端如下:

      左上部分是对数据库的显示,这里只显示了ID、昵称、在线状态。

        基于QT的网络聊天系统

       IP地址填写你的服务器主机的IP ,端口绑定8888,点击“开始监听”以后,就开始监听。发送按钮就可以把“hello client”发送出去。

     客户端界面如下:

        基于QT的网络聊天系统在登录和注册之前必须要设置服务器的IP地址和端口(和上面必修一致),在程序中已经写好,点击设置就可以,

        基于QT的网络聊天系统点击确定后

        基于QT的网络聊天系统才可注册和登录。

       点击注册新帐号后弹出注册界面

        基于QT的网络聊天系统填好上诉信息后确定即完成注册,然后在上面的界面中登录,登录后弹出绘画界面

        基于QT的网络聊天系统   下面是接受服务器的广播,上面是在线用户列表,点击其中一个弹出对话界面,

        基于QT的网络聊天系统“ssss”是自己发送的内容,有下面的文本框输入,点击“发送”后显示在上面,对方的回话也现在在此。

      4.tcp和udp消息的说明

         在具体设计和实现之前,首先列出内部通信的信息。客户端和服务器端的功能实现就是依靠接受不同的信息,判别后才完成相应的功能 

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 && index.column()==2)
  {

  value=(value.toInt()==1?tr(“在线”):tr(“离线”));
  return value;
  }

然后在引用QSqlQueryModel时,数据库里的0和1会自动转换成“在线”、“离线”。

      (3)正则表达式

          用户在界面输入的数据必须进行检验,检验合格后才能使用。比如ID号要求5~9位数字,IP号要求型如xxx.xxx.xxx.xxx等等。正则表达式准确书写比较繁琐,这里列出用的。

        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.代码上传

今天的文章基于QT的网络聊天系统分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/27366.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注