# 基于Java的聊天室系统
# 一 需求分析
编写一个小型Java聊天室系统,掌握Java网络通信、多线程、IO文件操作等高级应用编程技能。
完成如下功能:
- 多客户端模式下,实现客户与客户的单独通信,要求信息通过服务器中转;
- 端到端的通信,实现并行通信模式(一端的信息发送不受另一端的影响);
- 添加图形界面.
# 二 程序设计
## 2.1 设计思想
- 利用socket套接字通信
- 多线程处理不同任务
- 用Properties在本地存储注册账号
- 下载安装windowbuilder插件并用其设计图形界面
## 2.2 整体设计(类之间关系)
- Server类(服务器),包含几个继承Runnable的内部类,用于处理客户端请求
- Client类、Login类、TalkFrame类、Regist类。Login类用于登陆,Regist类用于注册,TalkFrame类用于对话,Client实例则被这三个类调用
- Account类,这个类很简单,只有id和password两个属性和相应的set方法。
类之间关系如下图所示:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/bbaea2f58bdb720e49cf53cbca43265c.writebug)
## 2.3 类的设计
1. **Client类**中只定义了Login、Regist、TalkFrame中需要的socket和流,和 对这些属性初始化的构造函数。
2. **Login类**用于登陆,包含账号label、密码label、账号JTextField、密码 JPasswordField、登陆按钮和注册按钮这些组件,还有一些监听器。
如下图所示:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/5f666bac2b505daa45f8a624c6543b73.writebug)
3. **Regist类**用于注册,包含用户名label、密码labl、确认密码label、用 户名JTextField、密码JPasswordField、确认密码JpasswordField和注册按钮这些组件,还有一些监听器。
如下图所示:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/dd1c4d44fb7374df8843ec3d20fc37e6.writebug)
4. **TalkFrame类**用于对话,包含选择对话用户label、UserList在线用户JscrollPane 面板、list用户列表、显示对话文本区域、发送信息文本区域和发送消息按钮这些组件,以及对应的监听器。TalkFrame中有两个继承了Runnable的内部类,分别处理在线用户显示和消息的发送。
如下图所示:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/356c0f2391e621b89d5570ac3e4f2ba8.writebug)
5. **Server类**有三个ServerSocket分别监听登陆端口、注册端口、显示在线用户端口。还有好几个继承Runnable的内部类,用于处理注册、登陆、显示在线用户、用户对话。
# 三 程序实现
由于涉及了账号注册的功能,必须能在本地保存账号,但我不会在Java中数据库插入数据库,所以只能想办法保存数据,一开始用的是hashtable,但保存到存盘有点困难,在网上搜到的方法是使用XML文件存取可序列化的对象的类,由于hashtable已经实现了序列化接口,所以可以这样实现,我用这种方法写了一遍,发现非常麻烦,再次百度,发现了properties这个集合类,查看JDK文档,了解了properties的load和store方法,这两个方法能很容易地将账号存储在磁盘的文件中。进而,我改用了properties存储账号。
```java
public Properties userInformation;
//与磁盘文件建立联系
userInformation = new Properties();
uis = new FileInputStream("f:/userInfo.properties");
uos = new FileOutputStream("f:/userInfo.properties", true);
userInformation.load(uis);
//将用户名和密码存储到内存中
userInformation.setProperty(account.getId(),account.getPW());
//将用户名和密码保存到文件中
userInformation.store(uos, null);
```
在处理server接收到的信息时,我用了三个ServerSocket进行处理,RegistServer监听规定的注册端口,对接收的注册信息进行处理;LoginServer监听登陆端口,对登陆后的对话信息进行规定,要求客户端传递的信息必须以接收者id+“:”+发送者id+“:”+发送信息的形式传递给服务器,服务器对传递过来的字符串进行截取,将信息发送给接收者客户端。
```java
监听发送消息按钮,在字符串前面加上规定的信息然后传递给服务器
sendButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
String str = sendMessageTextArea.getText();
String sendMessage = recieverId+":"+id+":"+"\n";
if(!sendMessage.equals("")){
client.pw.print(sendMessage);
client.pw.flush();
oldMessageTextArea.append(sendMessage);
oldMessageTextArea.setText("");
}
}
});
```
服务器截取信息,获取接收者id,找到对应socket,将信息传递过去。
```java
String s = br.readLine();
String accountId =s.substring(0,s.indexOf(":"));
String message = s.substring(s.indexOf(":")+1);
receiveClient = clientConnection.get(accountId);
PrintWriter pw= new PrintWriter(receiveClient.getOutputStream());
pw.println(message);
```
# 四 运行测试
登陆界面如下图:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/65ec954c82e49c63e5c66ccb4335366f.writebug)
注册界面如下图:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/89420aa8600d50dcf558674495003126.writebug)