- 有趣
- 涉及到java各个方面的技术
- 项目框架设计
- java面向对象
- 网络编程
- 多线程
- IO流
- Mysql / 使用集合充当内存数据库
- 巩固旧知识,学习新知识
弱化界面,因为界面会耗费很多时间,只需要掌握核心知识点即可
- 需求分析: 1)需求分析师:懂技术+行业 2)出一个需求分析报告(白皮书),该项目功能,客户具体要求
- 设计阶段 1)架构师/项目经理 2)设计工作(UML类图,流程图,模块设计,数据库,架构) 3)原型开发 4)组建团队
- 实现阶段 1)程序员/码农 2)完成架构师的模块功能 3)测试自己模块
- 测试阶段 1)测试工程师 2)单元测试,测试用例,白盒测试、黑盒测试、集成测试
- 实施阶段 1)实施工程师(开发能力/环境配置部署能力) 2)项目正确的部署到客户的平台,并保证运行正常 3)身体要好(经常出差)
- 维护阶段: 1)发现bug解决/项目升级
国内开发一般实现阶段会比较长,因为国内喜欢一边设计,一边开发
- 用户登录
- 拉取在线用户列表
- 无异常退出(客户端、服务端)
- 私聊
- 群聊
- 发文件
- 服务器推送新闻
因为还没有学习数据库,我们人为规定用户名/id = 100,密码123456就可以登录,其它用户不能登录
后面使用HashMap模拟数据库,可以多个用户登录
- 服务器端:
- 当有客户端连接到服务器后,会得到一个socket
- 启动一个线程,该线程持有该socket对象,也就是说socket是该线程的属性
- 为了更好的管理线程,需要使用集合hm来管理
- 客户端
- 和服务器端通信时,使用对象方式,可以使用对象流来读写
- 当客户端连接到服务端后,也会得到socket
- 启动一个线程,该线程持有socket
- 为了更好管理线程,也将该线程放入到集合
使用 Message 对象流的方式来进行通信,方便后面的拓展
思路分析:
- 客户端通过Message消息向服务端请求在线用户列表
- 服务端通过Message返回在线用户列表
思路分析:
- 客户端
- 客户端因为启动了接受消息的线程,因此在main线程退出之后,进程并不会结束
- 需要在退出系统选项处添加 System.exit(0)
- 另外在退出之前,需要向服务端发送一个断开连接的 Message
- 服务端
- 服务端在接受到客户端的 Message 关闭该线程
思路分析:
- 客户端:
- 接收用户希望给某个其它在线用户聊天的内容
- 将消息构建成 Message 对象,通过对应的 socket 发送给服务器
- 在他的线程(通信线程中),读取到发送的Message 消息,并显示即可
- 服务端:
- 可以读取到客户端发送给某个客户的消息
- 从管理线程的集合中,根据 message 对象的 getterid 获取到对应线程的 socket
- 然后将 message 对象转发给指定客户
和私聊差不多,只不过将Message 转给所有的线程而不是一个线程
功能说明:
- 发送给哪个用户
- 指定发送文件的路径
- 指定对方接受文件的路径
思路分析:
- 客户端:
- 先把文件 a.jpg 读取到客户端,字节数组
- 把文件对应的字节数组封装到 message 对象 [包含文件内容,sender,getter]
- 将 message 对象发送给服务端
- 在接收到包含有文件的消息后,就将该文件保存到磁盘
- 服务端
- 接收到 message 对象
- 拆解 message 对象的 getterid,获取该用户的通信线程
- 把 message 对象转发给指定用户
- 课后作业:让接收的用户指定保存的路径 思路1:
- 客户端接收到文件流之后指定保存位置。 这种思路有问题,因为会导致两个线程抢占控制台输入资源,导致不能从控制台输入文件存储路径
只是将普通的 Message 进行了拓展
功能说明 服务器可以推送消息给所有人
思路说明:
- 服务端
- 推送消息/新闻,本质其实就是群发消息
- 在服务器启动一条独立线程,专门负责发送推送新闻
在实际工作中,独立解决问题的能力非常重要,必须有意识培养
- 实现离线留言,如果某个用户没有在线,
- 实现离线发文件,如果某个用户没有在线,当登录后,可以接受离线的文件
思路:
- 服务端:离线发消息
- 当有客户发送消息/文件,如果用户不在线
- 把message存放到服务的 db [ConcurrentHashMap]
- key -> getterid value -> ArrayList, ArrayList 存放 message
- 当用户登录后,到服务端 db 去查找,如果有 getter = userid,就取出 ArrayList 的 Message 对象,发送给对应客户端即可
-
整体的框架是最难想的
-
处理流 ObjectOutputStream 不能随意关闭,因为关闭了处理流会导致内部的节点流也关闭,socket 也会被关闭,会和客户端断开连接
-
客户端和服务端的包名不一样,会导致使用对象流的时候出现classNotFoundException
-
在线用户列表是使用string来传输,中间用空格隔开,传输到客户端后再用split分离
-
客户端和服务端的联调比较困难,产生的bug比较难找
- 客户端:
- QQView 作为主线程接受用户的输入
- 在登录成功后启动一个线程接收服务端传递的消息
- 客户端这里是由两个线程在运行,一个接受用户的操作指令,执行命令,另外一个是接收消息
- 服务端
- 服务端线程 QQService 监听端口
- 如果有客户端连接,则建立一个线程保持通信,该线程接收来自客户端的请求,并处理该请求
- 客户端:
- QQView 向服务端发送请求,要求返回在线用户列表
- 另一个线程接收服务端返回的信息
- 服务端
- 服务端接收到客户端的请求
- 返回在线用户列表 1)创建管理线程的类,每次与客户端建立连接,则将连接线程放入该类的一个 ConcurrentHashMap 成员中,作为在线用户集合 2)用户发送请求的时候,取出在线用户集合的userId,通过空格连接成 String 类型,发送给客户端(客户端通过split空格解析)
- 客户端:
- 客户端在退出时发送消息告知服务端,需要退出登录
- 客户端调用 System.exit(0) 关闭程序
- 服务端:
- 服务端接受到客户端的退出登录请求后,将该用户从在线用户集合 ConcurrentHashMap 成员中移除
- 并调用 socket.close() 关闭连接,然后结束该线程
- 客户端
- 客户端发送消息给服务端,消息中带上 getter
- 服务端
- 服务端接受到消息,通过消息中的 getter 找到对应的连接线程,将消息转发
- 客户端
- 客户端发送消息给服务端
- 服务端
- 服务端接受到消息,遍历所有在线用户,转发消息
- 客户端
- 客户端将文件读入到内存中,在消息中设置byte[]数组,将文件流放入byte[]数组中
- 将消息发送给服务端
- 服务端
- 服务端接收到来自客户端的消息,将消息转发