type
status
date
slug
summary
tags
category
icon
password
多用户即时通讯系统
想起来很多年前写的一个 Demo,用 Java 实现了一个无界面端的通讯工具,简单回忆一下。
1,用户登录
为了实现服务器和客户端之间的消息交流,我先定义了三个服务器客户端共享的类或接口,分别是Message;User;MessageType接口,因为对象的传输需要对象流,所以要求对象实现序列化接口
1.服务端读取用户输入的用户信息,封装成User对象,通过:
将对象发送给服务端
2.服务端起一个serverSocket在配置的端口循环监听,每次建立连接就会获取对象输入流,读取客户端发送过来的User信息,判断用户是否合法,如果验证通过,就会获取一个对象输出流,发送一个message对象给客户端,设置messageType为LOGIN_SUCCEEDED,创建一个线程和客户端保持通讯,该线程持有socket对象和userID两个属性,启动线程并将线程放入集合中管理。如果验证不通过,就发送一个message对象并且设置messageType为LOGIN_FAILED。
3.客户端接收服务端返回的信息,如果验证通过就创建一个和服务器保持通讯的线程并且将当前的socket传给它,这就需要创建一个线程类,我命名为ClientConnectServerThread,这个类持有socket属性,编写run方法,无限循环从服务端读取信息。为了扩展,将线程放到集合中管理。需要创建一个ManageClientConnectServerThread类,这个类有一个HashMap属性key是userId,value是线程,编写一个方法,根据userId得到线程
2.拉取在线用户列表
1.客户端:因为服务端获取到一个消息时,会对它的类型进行判断,执行不同的业务逻辑代码;所以对于拉取在线用户的功能,客户端应该new一个message对象,设置messageType为GET-ONLINE-FRIEND,通过用户ID获取对应的线程,从这个线程里getSocket,再get输出流,用对象输出流包装,并将消息发送出去。 客户端监听从服务端返回的消息,如果消息类型是RETURN-ONLINE-FRIEND,就做相应的处理。
2.服务端:服务端监听从客户端发送的消息,如果消息类型是GET-ONLINE-FRIEND,就从管理线程的map里获取Key,key就是userID,获取到之后封装为message对象将消息类型设置为RETURN-ONLINE-FRIEND,通过userId get对应的线程,通过这个线程getSocket,通过socket得到输出流,用对象流包装,将message写入。
3.无异常退出
因为客户端的主线程开辟了一个无限循环接收消息的通信线程,客户退出主线程,但是这个通信线程不会自动退出,它还会一直运行。
解决方法:在主线程调用方法,给服务端发送一个退出系统的message对象,指定sender代表退出的userId,从管理线程的集合获取到线程,再获取到socket,再获取到输出流,用对象流包装,将message发送给服务器,再调用System.exit(0)结束进程;服务器如果接收到退出系统的message后,就把这个线程持有的socket关闭,并且将该线程从管理线程的集合中删除。
4.私聊
客户端:接收用户希望给某个用户发送的聊天内容,将消息封装成message对象,设置sender和getter属性messageType,通过对应的socket发送给服务器。在它的通信线程中读取到发送的message消息,并且显示即可。
服务器端:可以读取到客户端发给某个客户的消息,从管理线程的集合中,根据message对象的getterid获取到对应线程的socket,然后将message对象转发给指定客户。
5.群聊和服务器推送消息
在私聊的基础上,接收者变成所有用户,服务端遍历管理线程的集合,取出所有的userId,排除sender,获取所有的socket,通过socket获取输出流,用对象流包装,将message转发给所有人。
服务器推送消息就是给所有的用户发送一条信息,不在线的用户会在上线后第一时间收到信息。
6.发送文件
客户端:先把文件读取到程序中,得到字节数组,将字节数组封装到message对象,设置messageType和sender和getter,通过socket以对象流发送到服务端,服务端转发message给getter;getter的客户端接收到含有文件的消息后,将文件保存到磁盘。
服务端转发message给getter;getter的客户端接收到含有文件的消息后,将文件保存到磁盘。
最后客户端:
7.离线发送文件和消息
服务器: 当有用户发送文件,消息。如果接收者不在线,就把message存到服务器的一个concurrentHashMap,这个map的key就是getterID,value是一个存放Message对象的ArrayList<message>。每当有新用户登录时,服务器会查看这个集合里面有没有对应的userID,如果有,就遍历存放message对象的ArrayList<,message>,将文件发送给该对象,同时map.remove()这个键值对。
- 作者:流风回雪
- 链接:https://zone.lfhx.me/learning/java_qq
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。