源码已上传至Github↓↓↓

https://github.com/Arielxyx/Myim

一、需求分析

1. 客户端模块

客户端主要为用户提供了账号注册、登录系统、实时刷新所有在线用户、随意选择某一位用户私聊(包括自己)、聊天室群聊、本机存储与各用户的历史聊天记录、在修改用户名的同时更新该用户在列表上的用户名。

表1.1 客户端功能概述表

主要功能

功能概述

账号注册

未注册账号的用户可以通过手机注册:

填写手机号、密码、确认密码、用户名、年龄、姓名几个字段后;

注册成功则返会登陆界面;失败则提示失败原因。

用户登录

根据手机号和密码登录本系统:

填写手机号、密码;

登录成功则进入用户主页;失败则返回失败信息。

用户主页

显示信息

在主页最上方可显示用户名和手机号等基本信息。

修改密码

弹出框提示修改密码。

修改信息

显示并可修改基本信息,其中手机号被限制无法修改。

注销账号

清除本地登陆缓存记录,重新进入登陆界面后允许登录其他用户。

显示用户

实时显示所有与服务器连接成功且在线的用户列表。(用户上线、下线、修改用户名)

私聊功能

历史记录

选定用户即可私聊,自动显示于该用户的本地历史聊天记录。

发送消息

仅向私聊对象发送消息。

接收消息

可接收所有向自己发送的消息,但当前聊天窗口只显示该对象发来的消息。

群聊功能

历史记录

点击聊天室即可群聊,自动显示该群内所有的聊天信息。

发送消息

可和在线的所有用户聊天,所有在群里的在线用户都可以看到自己发送的消息,不在群内的用户进群之后也可以看到。

接收消息

可接收所有在线用户在群内发送的消息。

2. 服务器端模块

服务器端主要用于验证客户端的注册信息是否有效、验证登录信息是否正确、更新用户提交的信息修改内容、存储所有在线用户至列表、转发更新列表信息、转发聊天信息。

表1.2 服务器端功能概述表

主要功能

功能概述

验证注册信息是否有效

服务器端根据注册表单信息查询数据库,验证用户名和手机号是否已经被注册过。

如果信息有效则会将该用户插入数据库,否则向客户端返回无效信息。

验证登录信息是否正确

服务器端根据手机号和密码查询数据库,验证该用户是否存在、密码是否正确。

如果信息正确则登陆成功,否则向客户端返回无效信息。

修改用户存储在数据库中的密码

用户可修改密码,

下次再登录的时候就需要输入修改后的密码登录。

修改用户存储在数据库中的个人信息

用户可修改用户名、性别、年龄(手机号不可以修改),

该用户修改之后服务器不仅修改数据库的内容,还要将新的用户名发送给每个客户端,更新每个客户端的在线用户列表。

将在线用户转发给所有用户

当有用户登录本系统或有用户退出登录,则将新的在线用户列表传给每个客户端。

转发聊天信息

和自己私聊时,将消息仅发给传来消息的客户端。

和别人私聊时,将消息发给发送方和接收方。

群聊时,将消息发给所有客户端。

二、概要设计

在此说明每个部分的算法设计说明(可以是描述算法的流程图),每个程序中使用的存储结构设计说明(如果指定存储结构请写出该存储结构的定义)。

1. 用户信息处理部分框架简要介绍(SpringBoot+OKHttp)

1.1 服务器端 - 基于Mysql数据库使用SpringBoot框架整合Mybatis存储用户信息

1.1.1 设计如下用户表:

表2.1 User表设计

属性名

含义

类型

说明

id

主键唯一标识

int

每次自增1

phone

手机号

varchar

不为空

name

姓名

varchar

不为空

password

密码

varchar

不为空

sex

性别

varchar

不为空

age

年龄

int

不为空

1.1.2 创建SpringBoot项 - 构建如下目录结构:

注意事项:一定要加上需要的注解,加上之后才可以自动扫描包路径装载并注入对象,这样就无需繁琐的XML配置。Mapper层和Model层属性必须要和数据库一致否则无法映射成功

图2.1 目录结构
表2.2 SpringBoot层次结构及关系说明

层次

功能说明

Controller层

控制层,接收前端的业务请求,在调用业务层处理后返回相应的信息给前端

Service层

业务层,存放业务逻辑给控制层使用,不直接操作数据库而是调用mapper层的接口

Mapper层

映射层,对数据库进行持久化操作,通过注解可以更简单的操作数据库

Model层

实体层,存放实体类,属性要和数据库保持一致

 1.1.3 注入相关依赖 - springboot开发的起步依赖、mybatis、MySQL的JDBC驱动、generator等注入后,按下图配置服务器和数据库:

注意事项:由于没有购买云服务器,只能使用本机作为服务器,测试的时候在同一局域网下(连接同一个wifi)进行,所以这里的服务器连接地址是WLAN的IPV4地址。之后在测试的时候还需要关闭本机的防火墙,否则连接不上。

图2.2 服务器及数据库配置

1.1.4 配置generatorConfig.xml - 可以自动生成部分代码

注意事项:配置路径必须按照目录结构进行配置,否则会十分混乱。生成表的配置要按照数据库的表名和实体类名来设置。

图2.3 自动代码生成的相关配置

1.2 客户端和服务器端的交互使用OKHttp

1.2.1 安卓导入相关依赖并申请网络权限

图2.4 okhttp依赖注入

图2.5 网络权限申请

 1.2.2 将使用OKHttp请求的代码封装为一个工具类

在客户端先创建OkHttpClient对象,通过异步Post请求(添加多个键值对),使用Builder模式创建request对象传入formBody。之后在onResponse(…)回调函数中处理服务器传回的请求。

注意事项:这里请求的地址一定要按照服务器注解中的映射地址来配置,否则会报404错误。异步回调函数是在子线程中,不能在子线程中更新UI。回调的参数是Reponse类型,通过response.body().string()转化为字符串类型但这只能操作一次。

图2.6 回调处理

图2.7 请求封装

1.3 用户信息部分细节说明

注意:其中有的地方不仅涉及到OKHttp的前后后端交互,有两个地方也需要处理Socket管理端,用户登录时要判断是否重复登录;修改信息时需用Socket转发消息刷新列表。

表2.3 用户部分细节说明表

功能

说明

用户注册

分别查询用户名name和手机号phone是否已被注册

只有两个都没被注册才能插入数据库,并返回注册成功与否的信息

用户登录

首先利用sharedPreference检查本地缓存是否已经存储过用户信息,若查询得到则自动登录无需再输入账号密码

preferences = getSharedPreferences(getPackageName(), MODE_PRIVATE);
String result = preferences.getString("userinfo", "");

如果是第一次登录,信息正确则要加入缓存中

SharedPreferences.Editor editor = preferences.edit(); //用户信息存入SharedPreferences
editor.putString("userinfo", result);
editor.apply();

服务器端检验登录的时候不仅要在数据库中检查账号密码是否正确

若信息正确,还要检查该账号是否已经登录:通过判断客户端集合clientList内是否包含该user即可

User result = userService.selectByPhoneAndPwd( user);
if(result == null) { //密码或手机号错误,没有查询到
     return Constant.LOGINERROR;
}else if (ClientManager.nameList.contains(result.getName())) { //已经有用户登录
     return Constant.LOGINALREADY;
} else{
     return new Gson().toJson(result);

}

登录成功之后再连接服务器。(后面详细说明)

修改密码

自定义窗口弹出,无需再调用新的activity

修改基本信息

1. 当提交修改信息后,除了要通过OKHttp修改数据库

2. 修改本地缓存的用户信息

3. 修改当前程序中的临时user的用户信息

注意:通过OKHttp传来的用户姓名格式为:“用户名长度-修改前的用户名-修改后的用户名”,服务器端根据新修改的用户名向所有客户端发送信息要刷新在线用户列表

2. 客户端和服务器端使用Socket实时传送消息

2.1 设计一个MessageItem封装类

封装客户端和服务器端所有类型的消息(包括刷新列表类消息和聊天类消息)

表2.4 MessageItem封装类字段说明

属性名

含义

类型

说明

id

主键唯一标识

int

每次自增1

content

消息发送内容

String

不为空

time

消息发送时间

String

不为空

sendName

发送方姓名

String

不为空

revName

接收方姓名

String

不为空

sendId

发送方ID

int

不为空

revId

接收方ID

int

不为空

status

标识消息的状态

String

用户上线(online)/下线(offline)/信息更新(onlineupdate)/发给自己(myself)/发给对象others/发给聊天室(聊天室)

2.2 服务器端Socket编程管理类

表2.5 管理类属性说明

属性名

类型

说明

serverSocket

ServerSocket

用于服务器端监听特定的端口

socket

Socket

套接字:任何一个客户端发来请求都要构建一个socket

nameList

List<String>

存储上线用户名字的列表

messageList

LinkedList<MessageItem>

存储向客户端发送消息的列表

clientList

Map<Socket,User>

映射表可以得到每个socket对应的用户user

2.3 消息传递重点说明

表2.6 消息传递说明

要点

说明

流操作

读出

br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));

写入

pw = new PrintWriter(socket.getOutputStream());

数据类型转换

自定义对象

messageItem = new Gson().fromJson(receiveMessage, MessageItem.class);

Json字符串

String sendMessage = new Gson().toJson(messageItem);

客户端

发刷新列表消息

1.用户上线:登录成功一开始连接服务器 pw.println(new Gson().toJson(user));

2.用户下线(自定义了一个栈存储activity;下线时服务器将检测到不正常通信)

用户退出APP:关闭所有的activity

用户注销登录:要先清除本地用户缓存信息再关闭所有的activity

3.用户修改用户名:通过映射@PostMapping("/updateInfo")后,服务器端添加消息到链表中,消息属性为status=onlineupdate;content=新名称;sendName=原名称

发聊天消息

点击聊天对象时先向服务器通过用户名获取用户targetId(为了避免有用户修改用户名之后加载不出来历史聊天记录,所以聊天记录通过id来标识)

1.和自己私聊:status=myself;发送方和接收方都是自己

2.和别人私聊:status=others;接收方id为上述的targetId,name为顶部栏标识

3.群聊:status=聊天室;接收方无需定义

接收消息

1.为刷新列表:向OnlineListFragment广播,

2.为聊天记录:存储到本地Litepal数据库;向ChatActivity广播

服务器采用锁机制:

服务器端:当消息链表为空时,发送线程阻塞;当有消息需要传送的地方(更新用户名、聊天),唤醒发送线程。

客户端:没采用锁机制,因为不知道什么时候服务器会转发消息来,需要一直监听是否有消息传来。

服务器

收刷新列表消息

根据不同情况处理nameList、clientList、messageItem都需要将处理后的nameList拼接作为content

1.用户上线:位于线程的构造函数内

br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));

2.用户下线:用户退出APP、用户注销登录

发送一个字节的紧急数据,用于判定客户端是否还处于连接状态

收聊天消息 直接加入messageList即可

发送消息

当为刷新列表和群聊时:发送给所有客户端

和自己私聊:只发给自己

和别人私聊:发给对象和自己

三、可能遇到的问题

1. SpingBoot框架搭建时有哪些细节要注意?

(1)配置generatorConfig.xml时,tableName为数据库的表名或视图名,domainObjectName为实体类名,名称必须对应否则无法自动生成。

(2)需要注解的地方不能漏掉,否则对象注入失败。

(3)服务器配置便于测试,只能使用WLAN下的IPV4地址(处于同一局域网)。

2. 前后端交互时失败有哪些原因?

(1)本机未关闭防火墙。

(2)未连接同一个wifi。

(3)地址映射不正确,对应服务器端的协议类型、地址、端口号、映射地址检查一下。

3. 服务器端Socket编程整个流程是怎么样的?

答:首先用Serversocket配置服务器的端口号,并调用serverSocket.accept()监听socket(等待客户端来连接),若监听到了则为该客户端开启接收线程和发送线程,否则在该监听语句上阻塞。

4. 前后端使用socket传递消息时如何进行数据的获取和类型转化?

答:由于socket是基于文件流进行操作。读出的信息是字符串类型,便于操作我们可以传递json格式的字符串,将交换的消息封装为一个对象MessageItem,它们之间的转化可以导入Gson库来简化操作。这个消息有可能是刷新列表,也可能是聊天的消息,所以设置一个status表示消息类型。

5. 用户修改信息后各客户端的在线用户列表还是原来的名字?

答:当前客户端修改完名字后需要通知服务器端,服务器端接收到后转发给所有客户端有人改名字了,需要刷新列表了。

6. 为何消息列表出现错乱?

(1)在获取当前聊天记录的时候,标识判断不清晰,要根据发送方接收方以及消息状态判断。

(2)有用户修改名字后,虽然在线用户列表刷新了,但其他用户本地数据库的姓名并未改变,所以后来在MessageItem类中加上了SendId和RevId两个字段,获取历史聊天记录以及刷新聊天记录的时候都通过id来获取,这样名字改了也无所谓。

7. 用户退出时虽然finish()了activity但是并没有退出程序,导致用户列表混乱?

答:如果不使用栈来存储activity,就算finish了也可能会有漏网之鱼,所以当要退出的时候调用封装好的ExitApplication类中的exit函数销毁所有的Activity之后并退出系统。

8. 使用OKHttp编程时无法在回调函数内Toast?

答:异步回调函数是在子线程中,不能在子线程中更新UI。

基于Spring Boot整合Mybatis框架实现网络通信的即时聊天系统相关推荐

  1. 干货必看|Spring Boot整合MyBatis框架详解

    在开发中,我们通常会对数据库的数据进行操作,Sprirng Boot对关系型数据库和非关系型数据库的访问操作都提供了非常好的整合支持.所以今天壹哥就给大家讲解一下,如何在SpringBoot环境中整合 ...

  2. Spring Boot整合MyBatis框架(XML文件版)

    1.创建数据库.数据库表并插入数据 创建数据库springboot: CREATE DATABASE springboot; 创建数据库表user: CREATE TABLE `user` (`id` ...

  3. Spring Boot整合MyBatis框架(完整的注解版)

    1.创建数据库.数据库表并插入数据 创建数据库springboot: CREATE DATABASE springboot; 创建数据库表user: CREATE TABLE `user` (`id` ...

  4. Spring Boot 教程(三): Spring Boot 整合Mybatis

    教程简介 本项目内容为Spring Boot教程样例.目的是通过学习本系列教程,读者可以从0到1掌握spring boot的知识,并且可以运用到项目中.如您觉得该项目对您有用,欢迎点击收藏和点赞按钮, ...

  5. Spring Boot 整合MyBatis(23)

    Spring Boot 整合MyBatis Spring Boot 整合 Druid 引入依赖 配置 application.yml pring Boot 整合 tk.mybatis 引入依赖 配置 ...

  6. spring boot 整合mybatis 无法输出sql的问题

    使用spring boot整合mybatis,测试功能的时候,遇到到了sql问题,想要从日志上看哪里错了,但是怎么都无法输出执行的sql,我使用的是log4j2,百度了一下,很多博客都说,加上下面的日 ...

  7. TDengine 入门教程⑪——基于Spring Boot+Alibaba Druid框架的智能电表项目的后端时序数据库开发实战

    文章目录 一.前文 二.工程依赖 三.配置数据源 四.数据库连接池 五.电表数据实体类 六.数据库建表 七.业务Service层 八.总结 一.前文 TDengine 入门教程--导读 本开发实战配置 ...

  8. VUE+Spring Boot整合MyBatis实现前后端分离项目壁纸网站

    目录 前言 一.项目运行 二.环境需要 三.技术栈 四.项目说明 五.后端代码 前言 每次换桌面,壁纸总是不好找,搜索图片得不到好的索引与反馈,很难找到自己喜欢的壁纸,而壁纸网站可以免去我们去寻找壁纸 ...

  9. Spring boot 整合 Mybatis 实现增删改查(MyEclipse版)

    1.首先搭建好一个Spring boot 程序,编写好启动类. 启动类代码如下: @SpringBootApplication public class Start {public static vo ...

最新文章

  1. 复杂性系统面临的难题
  2. windows下把Apache加入系统服务
  3. android网页无法定位吗,在android中,js 无法定位 html页面的某元素
  4. 团队作业_1_博客1(分工理解)
  5. SQL Server中行列转换 Pivot UnPivot (转载)
  6. 信息学奥赛一本通 1139:整理药名 | OpenJudge NOI 1.7 15
  7. 【elasticsearch】block.ClusterBlockException: blocked by: SERVICE_UNAVAILA
  8. 学习编写测试桩之declspec (dllexport)篇
  9. Bootstrap_导航
  10. 密码学基础(二):对称加密
  11. 阿里云等企业主导的龙蜥社区发起“龙腾计划”;OpenInfra 基金会推出 LOKI 标准;GitLab 14.6 发布 | 开源日报
  12. Fujitsu Diagnostic(硬盘坏道检测工具)v6.8绿色版
  13. 详解示波器的三个主要参数:采样率,存储深度,带宽
  14. RADIUS服务器介绍
  15. 音频变速播放原理分析及实现方案
  16. 只有养老机器人才能拯救未来“老龄化中国”
  17. VScode创建第一个C++项目
  18. cdx 快速切换路径
  19. Java泛型 T extends Serializable
  20. 手机变板砖?这有专业救砖教程

热门文章

  1. 程序设计基础II-实验4-递归算法设计 7-5 喵帕斯之天才算数少女
  2. 怎样才能快速获取ABBYY FineReader 12中的注册码
  3. 《聪明的投资者》读书笔记
  4. 11个电子设计大赛的设计作品,会没有你想要的?
  5. 网络电话订购火车票全程体验
  6. 重庆青年建立个人网站 月收入约5万元
  7. a9 linux 程序下载,金山发布WPS for Linux A9下载
  8. 毅友汽修汽配管理软件 12.9 增强版破解补丁
  9. 自主飞行机器人的蜂拥算法
  10. 一级计算机基础知识考试第二套,一级考试计算机基础知识真题及解析.doc