C++服务器设计(七):聊天系统服务端实现

  在之前的章节中,我们对服务端系统的设计实现原理进行了剖析,在这一章中,我们将对服务端框架进行实际运用,实现一款运行于内网环境的聊天系统。该聊天系统由客户端与服务器两部分组成,同时服务端通过数据库维护用户的账号信息。本章将重点介绍如何运用该服务端框架进行服务器业务逻辑开发。

聊天系统功能分析

  本聊天系统只作为服务端框架的运用展示,因此仅限于最基本的局域网聊天工具,数据传输均采用为明文形式,并不在安全性上进行深入探讨。具体功能需求分析如下:

  • l  用户注册:提供新用户账号注册功能,注册内容包括新用户的账号名和密码。服务器需要检测注册信息的有效性,并将注册用户的信息保存在数据库中。
  • l  用户登录:用户根据账号密码进行登录操作,若未进行登录操作将无权限请求聊天相关消息。服务器查询数据库验证用户账号信息,同时判断该用户是否已经登陆。用户账号不允许重复登录。
  • l  在线好友更新:已登录用户可以主动向服务器查询当前在线的好友信息。服务器同样应该及时向所有登录用户推送好友上线及下线信息。
  • l  好友聊天:已登录用户可以向在线状态的任意好友发送聊天消息,同时也能接收到来自其他在线状态的好友发送过来的消息。
  • l  群体聊天:已登录用户可以同时向所有在线状态的好友发送群体聊天消息。同时也能接收到来自其他在线状态的好友发送的群体聊天消息。

聊天系统服务端实现

数据库实现

  聊天系统需要维护用户的账号信息,记录注册的新账号,并对每次登录的用户信息进行校验。我们选用轻量级的Mysql作为数据库管理系统,并选择Mysql++作为Mysql的API操作库。

  整个聊天系统只建立了一个名为UserInfo表结构,如表5-1所示。同时根据Mysql++封装了两个操作接口,分别添加新用户账号信息,以及根据账号名和密码查询该用户是否存在。

表5-1 用户账号信息表UserInfo

字段名

数据类型

关键字

约束

含义

id

int(20)

Y

unique

唯一标识符

username

varchar(20)

N

unique,not null

账号名

Password

varchar(20)

N

not null

密码

  由于数据库操作涉及网络传输及磁盘处理,因此属于耗时操作。而在服务端框架Reactor反应池中的所有处理必须在非阻塞环境下进行,如果进行耗时操作将会阻塞当前线程,导致其它消息事件得不到及时处理。因此在Reactor中进行数据库操作应该以异步的方式进行,可以通过创建工作线程池的方式处理数据库等耗时操作。但是在本聊天系统中,只有在注册新用户和用户登录两个场景中才涉及数据库操作。为了简化开发模型,我们在此直接调用数据库相关的操作。

  数据库部分并非本文论述重点,因此不再做进一步介绍。

聊天设备类型及消息事件实现

  通过分析功能需求,可以得知客户端连接主要存在两种状态下的消息请求,分别为临时状态和登录状态。在临时状态下需要能够请求注册新用户和登录操作,在登录状态下需要能够请求当前在线用户信息,向某个用户发送消息类型和广播消息类型。并且由于服务器存在登录超时的机制,客户端连接不管在临时状态还是登录状态,均需定时向服务器发送心跳消息。

  客户端的两种状态正好对应于服务端框架制定的两种设备类型,即临时设备类型和登录设备类型。如图5-1所示。我们为临时设备添加新的注册账号消息事件,用于向所有与服务端建立了连接的客户进行注册操作。同时我们创建一个新的设备类型,名为ChatType设备类型,并且继承于登录设备类型,用于专门处理登录后和聊天相关的消息事件。

 

图5-1 聊天设备类型及消息事件

  临时设备类型新添加的消息事件介绍如下:

  • l  registerUsr:注册新的用户账号。客户端在请求该消息事件时需同时传入期待注册的账号名和密码。服务器接收到该消息事件后,将校验账号名和密码是否合法,然后调用数据库新账户注册接口,进行账户注册。最后根据数据库实际插入结果向客户端发送注册结果信息。由于该消息事件无需请求权限,任何连接该服务器的客户均可进行注册操作。为了防止存在某些客户进行恶意注册现象,在实际实现中添加了邀请码信息。服务端将会验证客户端传来的邀请码是否正确,如果邀请码正确才会实际进行注册操作。
  • l  heartMsg:心跳消息。客户端将会定期发送心跳消息。服务器接收到该消息事件后,将会在超时队列更新该连接的超时信息。在事件回调实现中,并不会对该消息进行进一步业务处理,而是直接返回。

 

  ChatType为新创建的继承于默认登录设备的设备类型,具体实现的消息事件介绍如下:

  • l  askFriends:请求当前在线的用户信息。客户端会在登录成功后,向服务器请求该消息事件。服务器收到该消息事件后,将会查询除请求者外,当前其他所有在线的ChatType类型的用户信息,并整理发送给客户端。该数据用于客户端对在线用户列表的初始化工作。
  • l  chatSend:向一个指定在线用户发送聊天消息。客户端将会把聊天消息的内容,自己的信息,及期望接收该聊天消息的用户信息发送给服务器。服务端接收到该消息事件后,将会尝试找寻该接收用户,并将聊天消息和发送者消息转发给该接收用户,并向发送用户返回发送成功消息事件。如果该接收用户并不在线,将只会向发送用户返回发送失败的消息事件。
  • l  groupSend:向全体在线用户发送聊天消息。客户端将会把聊天消息及自身信息发送给服务器。服务器接收到该消息后,将会遍历除请求者外,当前其他所有在线的ChatType类型的用户,并将该聊天消息和发送者信息转发给所有遍历的用户,并向发送用户返回发送成功的消息。
  • l  heartMsg:心跳消息。同临时设备新添加的heartMsg的心跳消息。

聊天设备的生命周期实现

  虽然我们已经为聊天客户端的连接制定了具体的设备类型和消息事件,但是一些和连接状态相关的业务逻辑仍然需要我们设计。比如如何制定与聊天客户端相关的具体登录过程?当某个聊天客户端已经成功登录后,怎样第一时间通知其他客户端该账号已上线?当某个聊天客户端退出时,又如何通知其他客户端该账号已下线?这些与客户端状态相关的操作都依赖上一章节制定的连接生命周期机制来进行管理。

 

图5-2 聊天设备的生命周期

  具体的聊天设备的连接生命周期实现如图所示。我们只需对具体业务相关的生命周期接口的实现进行重写即可,因此无需进行更改的生命周期接口并未被列出。需要被重写的接口实现介绍如下:

  • l  onLoginCheckMsg():当客户端执行登录操作,且选择登录类型为ChatType设备类型时,将会执行该接口。此时系统将会检查客户端传来的账号名和密码是否合法,如果合法将会调用数据库的账号密码查询接口,判断该账户是否存在。如果该账户存在,则返回True,并将控制权交还给系统进行进一步的登录操作;如果该账户不存在,则向该客户端发送不存在该账号信息,此次登录失败的消息,并直接返回False退出整个登录操作。
  • l  onLoginSuccessMsg():当登录类型为ChatType的客户端进行登录操作,执行onLoginCheckMsg()返回True,且系统整个登录过程成功时,将会执行该接口。此时系统将会进行两件工作:首先向该客户端发送登录成功的消息;其次将会遍历除该用户本身外的其他已登录的ChatType类型的用户,并向这些用户发送通知消息表明该新用户已上线。这样每个登录用户只需在第一次登录的时候向服务器请求当前所有在线用户信息,而新的用户上线或老用户下线等信息将由服务器主动推送,而无需客户端定期请求维护。
  • l  onLoginFailureMsg():当登录类型为ChatType的客户端进行登录操作,执行onLoginCheckMsg()返回True,但整个登录过程失败时,将会执行该接口。一般该接口被执行将表明此用户账号已经被登录,服务器无法对相同账号进行重复登录操作。此时服务器将会向客户端发送重复登录,此次登录失败的消息,并退出整个登录操作。
  • l  onLogoutMsg():当登录类型为ChatType的客户端进行注销操作时,将会执行该接口。服务器将向该客户端返回注销成功的消息,并对该连接进行进一步的注销操作。
  • l  releaseConnNode():当登录类型为ChatType的客户端进行注销成功,或者连接由于超时或者其它异常原因被迫退出时,将会执行该接口。此时服务器将会遍历除该用户本身外的其他已登录的ChatType类型的用户,并向这些用户发送通知消息表明该用户已经下线。同时系统将会把该用户连接所申请的相关资源进行释放工作。
  • l  onOverTime():当服务器一段时间内未收到该客户端有效消息或心跳消息,将会被判超时,并执行该接口。一般执行该接口时表明该客户端已经由于异常而停止工作,但是并未主动断开连接。此时服务器将尝试向该客户端发送一条该连接已经超时的消息。由于客户端已经处于异常状态,并不能保证该条消息能够被客户端接收。然后服务器将会执行releaseConnNode()接口,并强制断开该客户端连接。

聊天系统展示

  该聊天系统部署于局域网内,并可通过C#实现的客户端进行相关操作。运行场景截图如下:

图5-3 登录及注册窗口

  如图5-3为客户端的登录窗口与注册窗口。注册窗口通过登录窗口的注册按键打开,并可进行新账号的注册操作。同时登录窗口可以进行账号的登录操作,如果登录失败将会显示具体失败原因;如果登录成功,将会进入客户端主界面。

 

图5-4 主界面及好友聊天窗口

  如图5-4为客户端的主界面及聊天窗口。在主界面中显示了当前登录的用户名及当前在线的用户名列及数量。可以单击具体用户名进入与该用户的聊天窗口。在聊天窗口中,可以在输入栏输入消息,并点击发送键或按下回车键发送该消息。如果收到其他用户的聊天消息,客户端也会主动弹出该用户的聊天窗口,并显示接收的的聊天信息。在主界面的下方还存在群聊按键,点击可以进入群聊窗口。

 

图5-5 群聊窗口

  如图5-5为群聊窗口。在群聊窗口中可以接收到打开了该窗口的其他用户发送的聊天信息。同时自己也能输入相关聊天消息并发送给所有该窗口下的其他用户。

转载于:https://www.cnblogs.com/moyangvip/p/5586045.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374519.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高校实验室管理系统_史上最全面的实验室信息管理系统(LIMS)全解

1. LIMS的基本概念和发展状况1.1 概括LIMS实验室管理系统是为实验、检测等业务板块提供流程化、模块化、标准化操作管理系统,打造基于行业法规的实验室全流程质量控制管理系统,实现实验室“人、机、料、法、环”关键环节管理。1.2 发展状况随着科研规范化…

ORM问题

在过去的几年中,像Hibernate这样的对象关系映射工具已经帮助开发人员在处理关系数据库方面取得了巨大的生产力增长。 ORM使开发人员可以将精力集中在应用程序逻辑上,并避免为诸如插入或查询之类的简单任务编写大量样板SQL。 但是,充分证明的对…

ActiveMQ中Session设置的相关理解

名词解释: P:生产者 C:消费者 服务端:P 或者 ActiveMQ服务 客户端:ActiveMQ服务 或者 C 客户端成功接收一条消息的标志是这条消息被签收。成功接收一条消息一般包括如下三个阶段: 1.客户端接收…

python中的 descriptor

学好和用好python, descriptor是必须跨越过去的一个点,现在虽然Python书籍花样百出,但是似乎都是在介绍一些Python库而已,对Python语言本身的关注很少,或者即使关注了,但是能够介绍把 dscriptor介绍清楚的&…

Heroku运行Java

如果您是像我这样的Java迷,那么您有个好消息值得振奋。 Heroku现在运行Java! 嗯,与其他流行的“ Web”语言(如PHP / RoR)不同,Java具有在Web服务器中进行部署和维护的麻烦。 一直以来,只有企业才…

配置了坐标还是找不到serv_为什么老人家总是这疼那疼,还找不到原因?是矫情还是另有原因...

“哎呀,怎么回事,腰痛腿痛的,痛的我一晚上都没怎么睡觉,怎么回事昨天也没干嘛啊!”对于这一句话,相信很多朋友都不是很陌生。这句话是来自于一位网友的留言,而说这话的真是她的妈妈。这也是很多…

Nginx负载均衡和LVS负载均衡的比较分析

lvs和nginx都可以用作多机负载的方案,它们各有优缺,在生产环境中需要好好分析实际情况并加以利用。 首先提醒,做技术切不可人云亦云,我云即你云;同时也不可太趋向保守,过于相信旧有方式而等别人来帮你做垫被…

QuartZ.net 常用配置说明

配置文件说明app.config中的quartz部分<quartz><!-- configure Thread Pool--><addkey"quartz.threadPool.type"value"Quartz.Simpl.SimpleThreadPool,Quartz" /><addkey"quartz.threadPool.threadCount"value"10&quo…

qopenglwidget 透明_廊坊透明真空袋用途-祺泰包装

功能方面&#xff1a;平面真空袋抽真空后易形成不平整&#xff0c;不均匀的现象。目前&#xff0c;真空包装主要用于食品的包装&#xff0c;如肉类、谷类加工食品以及易氧化变质的食品&#xff0c;也可用于机械零件、仪器和羽绒制品、毛制品等蓬松制品的包装。在超shi中&#x…

使用Spring Data Neo4j进行领域建模

大家好&#xff0c;威利在这里。 上次我告诉您&#xff0c;我正在使用Neo4j和Spring Data Neo4j构建Skybase CMDB&#xff0c;我很高兴收到很多对此的积极反馈。 我展示了一些代码&#xff0c;但没有那么多。 在本文中&#xff0c;我将向您展示如何使用Spring Data Neo4j在Skyb…

mysql 阿里内核人员

丁奇 http://dinglin.javaeye.com/鸣嵩 曹伟-鸣嵩 (新浪微博)彭立勋 http://www.penglixun.com/皓庭 http://wqtn22.iteye.com/项仲 http://blog.csdn.net/wudongxu剑川 http://gaoyusong.com/武藏 http://ybbct.iteye.com/祁奚 http://i.mtime.com/844165/褚霸 http://blog.y…

linux基本命令学习

1. 执行文件&#xff1a; ./文件 &#xff1a;执行该文件 sh startup.sh 执行该文件 2. Vi编辑 vi命令&#xff1a; :w 保存文件但不退出 :w file 将修改另外保存到file中&#xff0c;不退出 :w! 强制保存&#xff0c;不推出 :wq 保存文件并退出 :wq! 强制保存文件&#xff0…

506. 相对名次

方法一 复制数组排序返回对应下标 class Solution {public String[] findRelativeRanks(int[] score) {int n score.length;int[] newScore Arrays.copyOf(score,n);Arrays.sort(newScore);String[] res new String[n];for(int i 0;i<n;i){int rank Arrays.binarySear…

Java压缩

在最近的项目中&#xff0c;我们不得不做一些我个人从未真正看过的事情。 压缩。 我们需要拍几个文件和图像&#xff0c;将它们压缩并提供给FTP使用&#xff0c;是的&#xff0c;总有一天&#xff0c;感觉确实回到了90年代。 除了过去的FTP之行外&#xff0c;它还是一个很好的机…

苏州面对公司发布

假设您对我们这种创业型公司和我们的发展方向感兴趣的话&#xff0c;我们希望通过以下10个问答进一步添加两方的了解。我们希望看到的是您经过深思熟虑的、对公司和自己的前途负责任的谨慎回答。而不是应付公差式的轻描淡写&#xff08;我们会依据您回答质量的高低决定是否邀请…

linux多线程_Java+Linux,深入内核源码讲解多线程之进程

之前写了两篇文章&#xff0c;都是针对Linux这个系统的&#xff0c;为什么?我为什么这么喜欢写这个系统的知识&#xff0c;可能就是为了今天的内容多线程系列&#xff0c;现在多线程不是一个面试重点 啊&#xff0c;那如果你能深入系统内核回答这个知识点&#xff0c;面试官会…

594. 最长和谐子序列

和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 现在&#xff0c;给你一个整数数组 nums &#xff0c;请你在所有可能的子序列中找到最长的和谐子序列的长度。 数组的子序列是一个由数组派生出来的序列&#xff0c;它可以通过删除一些元素或不删除元素、…

解决git clone报错SSL certificate problem

Git新手一枚&#xff0c;今天进行git clone操作时发生如下问题&#xff1a;提示无效的链接error: SSL certificate problem: Invalid certificate chain while accessing https://githib.com/...XXXX.git fatal: HTTP request failed解决方法也很简单&#xff0c;一条命令就搞定…

使用内存映射文件获取巨大的矩阵

总览 矩阵可能真的很大&#xff0c;有时甚至比一个数组中可以容纳的更大。 您可以通过具有多个数组来扩展最大大小&#xff0c;但这会使堆大小确实很大且效率低下。 一种替代方法是在内存映射文件上使用包装器。 内存映射文件的优点是它们对堆的影响很小&#xff0c;并且可以由…

ipad连接电脑_这些应用让iPad生产力分分钟UP

IT时报见习记者 钱奕昀用iPad办公这件事&#xff0c;多年前网友就在讨论&#xff0c;最常见的还是那句“买前生产力&#xff0c;买后爱奇艺”。很长一段时间里&#xff0c;它的生产力属性都是弱于娱乐属性的。其实&#xff0c;作为PC端和移动端的形态中和&#xff0c;iPad可以…