【Java学习】聊天室项目(32)

聊天室项目需求:
1、用户名登录注册(判断有没有重复用户名,可设置ip和端口)
2、上下线提醒
3、在线列表
4、私聊
5、公聊
6、发送文字,文件。
7、聊天记录 保存 查询 删除。
8、下线

选做功能
1、切换状态(在线,隐身,忙碌)
2、建群
3、大文件传输。(新建流传输)*
4、离线消息*
5、个人信息设置
6、语音聊天*
7、朋友圈
8、文字表情
9、增删好友,修改好友备注名。

1.首先你得写一个客户端,一个服务器端,利用TCP协议连接,先实现客户与服务器端聊天,我们开启子线程专门负责接收消息,主线程负责发消息.如果你还不会 点击这里

2.然后这只是初期,我们一步一步来,我们是不会和服务区聊天的,服务器只是用来转发消息的,我们多个用户连接到服务器上,所有消息通过服务器转发给接收者,所以我们得在服务器端设置一个循环监听,若有客户端连接,则就为他开启一个子线程,将他的名称和他所对应的管道(socket)存到集合(可用hashMap双列集合)中去,这样的话,每一个客户端自己都有一个主线程负责发送消息,都有一个子线程负责接收消息,客户端连接上服务器后,服务器为每个线程开启一个子线程,给客户端提供服务.

3.若李四和王五都已经连接了服务器,当李四要给王五发消息时候,先将消息发给服务器,经由服务器转发给王五,所以此时要规定消息格式,要让服务器知道这个消息是给王五的,不是转发给赵六的,例如规定格式为 接收者#消息内容#消息标志位#发送者,类似的格式,这样服务器端根据已经定好的格式,对收到的字符串进行按#截取,就可以拿到接收者是谁,消息是什么,等信息.

4.在改进一下,再服务器端再写一个注册线程,再写一个登陆线程,客户端连接上服务器的时候,让他选择是登陆还是注册,进入不同的子线程,当登陆或注册成功后,自动开启服务器端子线程,为客户端提供服务,在客户端连接的时候,先进行注册,让用户端输入用户名,输入密码,(为后面写登陆做准备)服务器端就接受到后,(用Properties属性集合)对这种用户名密码的键值对数据用文件(以后可用数据库)保存起来,当注册成功后,再开启服务端子线程,为客户端提供服务.

5.当用户上线之后,应该对所用的现在用户发送上线提醒,某某某上线了.所以刚才当客户端登陆的时候,将他们们的信息保存再集合里面就用到了,就可以遍历集合,给别的人,发送上线提醒.

6.在客户端注册代码之后(此时已经连接上客户端了,客户端子线程也开启了,为你提供服务了),客户端主线程再写一个switch-case可以用于功能的选择,比如你要私聊,还是公聊,要发文件,切换状态,查看聊天记录,退出等功能.

下面我说一下写这些功能我的想法,每个人的想法不一样,求同存异啊.
首先需要定义一些常量,用于判断该开启那个功能,在客户端请求服务器的数据中加上这个常量,服务器端收到数据后,对数据进行以之前规定好的#号拆分,根据拆分得到的常量(单独写一个类,对每一个功能都定义一些静态常量,利用这些常量,来区别功能,让服务器知道,你发来的请求是干什么用的,或者使用枚举也可以)就可以判断该是私聊,还是公聊,这种的功能选择.

服务器端的集合中存有每个在线的人的管道(socket),私聊就是,将消息通过管道获取流(getOutputStream)转发给收信者,公聊是遍历集合,转发给所有在线的人.获取在线列表就是将集合中的用户名,拼接成一个字符串当作服务器的消息,转发回来.
要做隐身功能的话,可以在new一个单列集合arrayList或者hashSet,在用户登陆成功或注册成功时候,不仅将用户名和对应的管道存到双列集合中,而且再将用户名存进单列集合中,在切换状态的时候,只需要将单列集合中的用户名删除,在获取在线列表的时候,将原来遍历双列集合变为现在遍历单列集合,双列集合不动,只用作公聊的时候用,隐身也是可以收发消息的,只是别人获取在线列表的时候获取不到.

在收发文件时候,利用一个小技巧,将前10kb用作每次发送的指令消息(接收者#消息#消息标志位#发送者)的字节数,在将文件的字节数加到这10kb后面,当服务器收到的时候,将缓冲区设置为10kb,都是先一次性读取10kb消息指令,当读到的消息之类中的消息标志位位发文件的时候,就跳到收文件的功能上,在这个方法中再次读取文件部分,这种方法可以满足,发送较小的文件mb级别的.这其中用到了内存操作流(ByteArrayOutputStream())可以用来拼接字节.如果传送的文件比较大,可以考虑先上传到服务器端保存,再由接收者从服务器端下载.
收发文件会有一个问题,就是,你的客户端子线程是收信息的,当客户端反馈回消息,有人给你发来了一个文件,此时你是没法输入信息的,因为你的主线程现在停留再录入消息的状态上,所以子线程没法输入消息,此时可以写一个共享变量,利用主线程的输入,改变共享变量的值,子线程循环读取共享变量,若共享变量变了,则读取break,这样以达到子线程获取输入的数据的方法,点击详见

代码运行图示:
在这里插入图片描述
在这里插入图片描述

代码太多,我打包在github中了


谢谢

猜你喜欢

转载自blog.csdn.net/qq_43371004/article/details/87732149