目 录
1 功能设计 1
1.1功能概述 1
1.2功能模块图 1
2 逻辑设计 2
3 界面设计 4
3.1注册界面: 4
3.2登录界面 5
3.3好友列表页面 5
3.4好友聊天页面 6
3.5服务器界面 7
4 各模块详细设计 7
4.1登录模块 7
4.2注册模块 9
4.3聊天模块 10
4.4数据库工具类 12
4.5封装的数据交换类 12
5 系统测试 13
6 主要问题解决过程 15
7 作品总结 16
1.1功能概述
该系统是基于JAVA语言开发的一款网络聊天工具,通过Socket实现TCP编程,使用多线程实现了多客户端的连接。模仿腾讯QQ的界面,功能较为简单,但是使用了最基本的网络编程技术,如socket、tcp、I/O阻塞、多线程、MySQL数据库等。
该系统实现了用户的注册、登录以及与好友聊天的功能。
用户注册:系统随机生成账号,用户按照要求填入相应的信息。
用户登录:用户根据自己的账号和密码进行登录。
好友聊天:登录成功后,点击好友列表中的好友进行聊天。
6 主要问题解决过程
1)如何判断该好友是否在线?
最后一节课的课程分享,5班的同学在他的课程设计中使用了数组对登录的用户进行了记载,0代码不在线,1代表在线。在这里,我使用了相同的处理方式,将所以已经成功登录的用户SocketThread加入到HashMap集合中,SocketThread中记录了所有成功连接的socket实体类。
2)消息只能接收一次
使用while循环,时刻监听socket发送的数据,I/O阻塞接受客户端的消息。
3)验证登录和验证注册的逻辑使用了同一个。
一开始写好了登录的验证,后来实现注册时,在想注册和登录不就是返回结果相反吗?登录时是通过服务器查询数据库中存在该账号才允许登录,而注册验证是查询数据库后没有该用户才允许注册,所以直接调用了同一个方法。后来注册时有时可以,有时出现问题,当时没有仔细想,后来才发现,本文转载自http://www.biyezuopin.vip/onews.asp?id=14869返回结果是相反,但是验证注册不需要密码啊,只需要查询数据库中是否有该用户即可,如果使用登录的验证进行注册验证,那用户账号一样,密码不一样,同样可以允许注册了。
将注册的验证和注册的完成操作单独拿出,bug解决。
4)如何将不同的请求进行集中操作?
Java中的泛型可以处理不同数据的传入,但是由于平时使用的不多,对于泛型并不能彻底为我所用。最后封装了一个CommandTranser对象,实例化客户端时,通过sendData(CommandTranser msg)方法向服务器发送数据,然后通过getData()方法获得数据库返回的结果。
5)如果好友不在线,发送的消息怎么处理?
一开始自己想,只要好友不在线,弹出系统提示就足够了,不需要再将消息进行处理,可以在运行时出现了问题。没有想到好的解决办法,自己的处理方式是当好友不在线时,将该消息的发送方和接收方都设置为了自己。其实后来想想,如果实现了在线好友列表,在未发送消息之前,就可以知道好友是否在线,该问题就迎道而解。
package cn.edu.ldu.ui;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import cn.edu.ldu.socket.Client;
import cn.edu.ldu.socket.ClientThread;
import cn.edu.ldu.util.CommandTranser;
/**
* 聊天对话框
*/
public class ChatUI extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private JTextArea chat_txt;
private JTextField message_txt;
private JButton send_btn;
private JPanel panel;
private String owner;
private String friend;
private Client client;
private ClientThread thread;// 接收信息线程
public ChatUI(String owner, String friend, Client client) {
this.owner = owner;
this.friend = friend;
this.client = client;
init();
setTitle("与" + friend + "聊天中");
setSize(350, 350);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
// 开启客户端接收信息线程
thread = new ClientThread(client, chat_txt);
thread.start();
}
private void init() {
// TODO Auto-generated method stub
setLayout(new BorderLayout());
panel = new JPanel();
message_txt = new JTextField(20);
send_btn = new JButton("发送");
panel.add(message_txt);
panel.add(send_btn);
chat_txt = new JTextArea();
chat_txt.setEditable(false);
chat_txt.add(new JScrollBar(JScrollBar.VERTICAL));
add(chat_txt, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
send_btn.addActionListener(this);
// 添加窗口关闭事件
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// TODO Auto-generated method stub
thread.setOnline(false);
}
@Override
public void windowClosed(WindowEvent e) {
// TODO Auto-generated method stub
thread.setOnline(false);
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// 如果点击了发送按钮
if (e.getSource() == send_btn) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss a");
String message = "你说:" + message_txt.getText() + "\t"
+ sdf.format(date) + "\n";
// 在本地文本区追加发送的信息
chat_txt.append(message);
// msg为客户端向服务器发送的数据
CommandTranser msg = new CommandTranser();
msg.setCmd("message");
msg.setSender(owner);
msg.setReceiver(friend);
msg.setData(message_txt.getText());
client.sendData(msg);
// 发送信息完毕 写信息的文本框设空
message_txt.setText(null);
}
}
}