我是一个新手,学习了Socket通信后,寒假花了20天写了这个小项目,只有一个客户端,而且也是一个尚未完工的客户端,服务器端只用来接收,转发或保存消息。本不准备发出来的,因为项目还在编写当中,实现的功能不多,且一些细节还没有处理好,以后还会再写一个比较细致的版本,不过老师要求了,就动手写了这篇总结。
项目名称:大山QQ,用以纪念我的大三。
项目意义:对相关知识点的一个综合练习,熟悉Java通信方面的类的用法及组件的应用,记录我大三的学习痕迹。
通信协议:字节流协议
功能:
1.注册
2.查找
3.添加/删除好友
4.添加/删除分组
5.好友上下线提醒
6.发送在线离线消息
7.发送在线离线文件
界面:全部采用空布局,所有组件都是通过setBounds(....)方法添加的
具体流程:
服务器端:
服务器端还没有怎么写,只实现了基本的消息传送功能。
开启服务器以后,首先读取所有的用户信息,等待客户端的链接。由于尚未学习数据库,所有的用户信息都是
以文件形式保存的。当开启服务器后,读取的所有用户信息都保存在辅助静态类ChatTool的一个用户Map<User_ID,User>中,当客户端连接上后,若为登录消息且ID和password都正确,则将Client_Thread(服务器端的用户通信线程类)添加至ChatTool类的线程Map<User_ID,Client_Thread>中,当客户端向流中写入消息后,对应服务器通信线程读取该消息,并作出对应操作。
客户端:
1.登录
账号密码框只能输入10位以内的数字0到9,实现方法有两种 1.添加DocumentListener 2.添加keyListener
具体见包MyComponent下的MyJTextField类
界面中的找回密码,设置只是按钮,还未添加功能。
若是账号密码均正确,服务器回发个人信息,包括账号,签名,头像,以及好友信息,未读消息等,建立缓存,存储好友的头像,然后转入好友列表界面。
这一步有待改进,头像的字节长度远远大于其他信息的字节长度,在本机建立好友头像缓存,可以减少信息的传输,提高通信速度。
改进方法:登录前检测本机好友头像的个数,传送登录信息时加上本机好友头像的好友ID,服务器只回发其他好友的头像以及更改了头像的好友头像即可。这一步更麻烦了,不过通信效率的提升很可观。
2.注册
做注册界面时,重要的就是在发送注册消息前先检测输入的数据是否合法,并予以提示
当所有的信息都无误了,再向服务器发送消息。若注册成功,服务器端保存用户信息,我最开始是用一个文件保存的,
后来做其他功能时发现要修改用户信息时会很麻烦,后改为每一项信息保存为一个文件。
注册成功,回发消息,提示,返回登录界面。
做注册功能的时候,我意识到应该要有容错处理,一切数据都合法的情况下再提交至服务器。
3.好友界面
界面上的所有图标都是JButton,setIcon(icon),没有什么用处,主要是仿QQ,以后添加功能方便些
JTree显示好友,头像,账号,昵称,是否在线,签名
具体功能有5个
1.添加/删除分组 2.删除好友 3.搜索用户(search) 4.双击好友头像,弹出聊天界面
4.用户查找界面
ID查找和昵称查找等的实现方法是相同的,所以就只做了一个ID查找
服务器读取要查找的用户ID,若存在就回发该用户ID,昵称等模糊查询回发的就是所有满足搜索条件的用户信息
当点击添加好友时,服务器接收到消息后首先检查被添加用户是否在线,在线发送消息,不在线就在被请求用户未读消息目录下的聊天消息下生成一个文件,写入消息。当被请求者用户上线时,服务器发送其信息时包含这些未读消息,同时删除未读消息文件。
这是另一个我觉得很重要的地方----要保证信息的不丢失
比如A请求添加B为好友,首先A向服务器发送请求消息,为保证B收到请求, 若B不在线,则将请求保存到B的未读文件中,在其上线时读取自身信息时(服务器在发送完温度消息后删除该消息),在通知B,若B在线则直接向其发送消息。B接收请求,选择分组,刷新界面,同时向服务器发送消息,服务器在保存信息,同时通知A结果,A在选择分组,刷新界面,再将结果发到服务器保存。
就是将客户端的一切改动都发到服务器保存下来。做修改个人信息的功能的时候,比如修改昵称,签名,密码等也是一样的
过程。
5.好友上下线提醒
我觉得这个功能的实现是这个项目最有价值的地方。采用了观察者模式。
具体做法如下:
自定义一个接口MsgListener,含有方法ReceiveMsgAction(Msg msg);
自定义一个接口subject,含有方法addMsgListener(MsgListener l),removeMsgListener(MsgListener l),
fireMsgListener(Msg msg);
客户端的通信线程类ClientThread继承Subject
定义类MyJTree_AsListeber extends JTree implements MsgListener ,重载ReceiveMsgAction(Msg msg),创建对象时,将之注册到对应的通信线程上。当对应通信线程类对象调用fireMsgListener(Msg msg)时,通知JTree对象作出相应显示刷新。
之前我一直都很追求外观,可当老师第一次讲到设计模式的时候,我才意识到纯粹的最求外观是没什么意义的,最重要的还是程序的结构,一个好的软件的设计应该是多种结构的综合,结构的好坏决定了程序的拓展性和可维护性。至于技术点,最好是能读懂源码,理解原理,这需要经验,现在的我还差得远,对于技术只停留在用的阶段,还未上升到理论。
6.聊天界面
视频语音截图震动功能都没实现
可以修改字体属性,传送消息,文件。未保存消息
震动的具体原理就是setLocation(x,y)方法的运用
截图,远程就是Robot类的应用
视频功能需要JMF的相关知识,还没有研究
7.传送文件
流程如下:1.在线文件的传送 先发送请求,对方同意后,开启一个服务器(有堵塞,应该放在线程中),回发给发送者,然后发送者连上该服务器,开启多个线程(我开启的是2个线程),分段传送文件,具体实现是RandomAccessFile类。
如DataImputStream类对象只能从文件的第一个字节开始读,但RandomAccessFile类对象可以随机读取文件,调用seek(x),或者skipBytes(X)方法,改变指针位置,就可以实现随机读取某一位置的字节。迅雷下载就是这种原理。开始,不清楚seek(x),或者skipBytes(X)方法是如何改变文件指针的,是读取X个字节再丢掉,还是直接改变指针在内存中的地址呢?经过测试,答案是后者。 2.离线文件的传送 由于只有一个通信I/o,传送文件应该另外创建Socket对象连上服务器,发送完文件后再关闭Socket。
项目的不足:
1.数据的存储 文件存储终究不如数据库存储,读取速度慢,性能低。只适合小群体的聊天
2.结构 虽然尝试使用了观察者模式,但客户端程序的耦合度还是比较高的,一个好的结构应该是多种设计模式的综合
3.功能 功能比较少,而且有缺陷,比如还没有加上远程,视频等功能,前几天已经把远程写好了,不过对图像的压缩度
感到不满意,其他功能的细节也有待改进,比如在添加好友时,我没有先判断是否请求对象已是自身好友等,都
是些细节的地方,还有修改个人信息的功能等等,这是都不难,但这些细节都决定着项目的友好性。很重要。
4.有一个问题没有解决,好友查找界面,表格刷新时会包空指针的错,没找到具体原因。
5.代码的结构不是很好,注解写的少了
6.没有创新 纯粹是技术的练习,尝试使用了一个设计模式,没有创新,没什么价值
收获:
1.意识到结构和友好的重要性
2.在调试的过程中,第一次意识到Java程序对系统内存,CPU的占用问题,意识到了一些小问题,比如读写文件不关流的话,就不会释放内存。
3.让我的心情很平静,不再浮躁
注:项目中的所有图片都是截图截下来的,在压缩包的图片文件夹中。