本聊天系统包括三个部分,客户端、服务器以及数据库的连接,客户端包含了5个java文件,分别为主类client.java、登录窗口类login.java、上线提醒类onlinewindow.java、消息提示音类playWAV.java、注册类Register.java.数据库包含一个数据库连接文件DB.java,服务端包括4个java文件,分别是客户端服务线程类ClientThread.java,服务端线程类文件ServerThread.java,服务端界面设计类ServerUI.java,用户信息类User.java.
数据库使用了一个hellouser表用来记录用户注册和登录的账号密码。
项目网盘链接:链接:https://pan.baidu.com/s/1LkfLI7TWcZz4TiFENEx80w
提取码:bgmz
功能模块设计
①登录功能
需要提供支持用户登陆以及身份验证的WEB界面和实现后台对应功能的模块
②注册功能
需要提供支持用户注册用户名和密码且密码要输入两次相同的模块
③私聊
用户选择在线的某一个用户进行聊天,且不能和自己聊天或空发消息模块
④群聊
用户选择“全部”输入想要发送的消息,点击发送模块
⑤上线提醒
当某个用户上显示有小窗口提醒某个用户上线模块
⑥消息接收提示音
当有用户发送消息给另一个用户时,另一个用户会听到提示音消息,提醒有消息收到模块
代码(部分)
Client.java
// 连接服务器
private void ConnectServer() {
try {
socket = new Socket(ip, port);// 根据端口号和服务器IP建立连接
writer = new PrintWriter(socket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// 发送客户端基本信息(用户名和IP地址)
sendMessage(name + "@" + socket.getLocalAddress().toString());
// 开启不断接收消息的线程
messageThread = new MessageThread(reader);
messageThread.start();
isConnected = true;// 已经连接上了
JOptionPane.showMessageDialog(frame, name + " 连接服务器成功!");
frame.setVisible(true);
} catch (Exception e) {
isConnected = false;// 未连接上
JOptionPane.showMessageDialog(frame, "连接服务器异常:" + e.getMessage(),
"错误", JOptionPane.ERROR_MESSAGE);
}
}
// 断开连接
public synchronized boolean ConnectClose() {
try {
sendMessage("CLOSE");// 发送断开连接命令给服务器
messageThread.stop();// 停止接受消息线程
// 释放资源
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
if (socket != null) {
socket.close();
}
isConnected = false;
return true;
} catch (IOException e) {
JOptionPane.showMessageDialog(frame, name + " 断开连接服务器成功!");
isConnected = true;
return false;
}
}
Login.java
frame = new JFrame("欢迎来到聊天室!");
// 设置窗体的标题图标
Image touxiang = new ImageIcon("image/icon.png").getImage();
frame.setIconImage(touxiang);
// 背景图片
background = new ImageIcon("image/bg.jpg");
JLabel label = new JLabel(background);// 把背景图片显示在标签里
label.setBounds(0, 0, background.getIconWidth(),
background.getIconHeight());// 把标签的大小位置设置为图片刚好填充整个面板
// 把内容窗格转化为JPanel,才能用方法setOpaque()来使内容窗格透明
imagePanel = (JPanel) frame.getContentPane();
imagePanel.setOpaque(false);
imagePanel.setLayout(null);// 设置布局方式为绝对定位
onlinewindow.java
public void run() {
frame = new JFrame("上线提醒");
frame.setIconImage(new ImageIcon("image/icon.png").getImage());// 更改JFrame的图标
showMsg = new JTextArea();
showMsg.setText(" "+name+" 上线啦!");
showMsg.setFont(new Font("宋体", Font.BOLD, 25));
showMsg.setEditable(false);// 显示消息框不可编辑
showMsg.setForeground(Color.WHITE);
playWAV.java
public void Play(String s) {
this.str = s;
try {
AudioInputStream ais = AudioSystem
.getAudioInputStream(new File(str));// 获得音频输入流
AudioFormat baseFormat = ais.getFormat();// 指定声音流中特定数据安排
// System.out.println("baseFormat=" + baseFormat);
DataLine.Info info = new DataLine.Info(SourceDataLine.class,
baseFormat);
// System.out.println("info=" + info);
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
// 从混频器获得源数据行
// System.out.println("line=" + line);
line.open(baseFormat);// 打开具有指定格式的行,这样可使行获得所有所需的系统资源并变得可操作。
line.start();// 允许数据行执行数据 I/O
int BUFFER_SIZE = 4000 * 4;
int intBytes = 0;
byte[] audioData = new byte[BUFFER_SIZE];
while (intBytes != -1) {
intBytes = ais.read(audioData, 0, BUFFER_SIZE);// 从音频流读取指定的最大数量的数据字节,并将其放入给定的字节数组中。
if (intBytes >= 0) {
int outBytes = line.write(audioData, 0, intBytes);// 通过此源数据行将音频数据写入混频器。
}
}
Register.java
private void registerUser(String name, String pwd1, String pwd2) {
if (pwd1.equals(pwd2)) {
UserDB c = new UserDB(name, pwd2);
if (c.addsql() == true) {
JOptionPane.showMessageDialog(new JFrame(),
"注册成功!\n请记住您的账号和密码", "恭喜", JOptionPane.CLOSED_OPTION);
} else {
JOptionPane.showMessageDialog(new JFrame(), "注册失败,换个用户名试试吧!", "错误",
JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(new JFrame(), "两次输入密码不相同!", "错误",
JOptionPane.ERROR_MESSAGE);
password.setText("");
password2.setText("");
}
}
DB.java
public class UserDB {
// 驱动程序名
String driver = "com.mysql.jdbc.Driver";
// URL指向要访问的数据库名hello
String url = "jdbc:mysql://xxx:xxx/hello";
// MySQL配置
String sqluser = "root";
String sqlpassword = "xxx";
ClientThread.java
public ClientThread(Socket socket) {
try {
this.socket = socket;
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
writer = new PrintWriter(socket.getOutputStream());
// 接收客户端的用户信息
String info = reader.readLine();
StringTokenizer st = new StringTokenizer(info, "@");
user = new User(st.nextToken(), st.nextToken());
// 反馈连接成功信息
// writer.println(user.getName() + user.getIp() + "与服务器连接成功!");
// writer.flush();
// 反馈当前在线用户信息
if (ServerUI.Clients.size() > 0) {
String temp = "";
for (int i = ServerUI.Clients.size() - 1; i >= 0; i--) {
temp += (ServerUI.Clients.get(i).getUser().getName() + "/" + ServerUI.Clients
.get(i).getUser().getIp())
+ "@";
}
writer.println("USERLIST@" + ServerUI.Clients.size() + "@"
+ temp);
writer.flush();
}
ServerThread.java
public void run() {
// 循环等待客户端的链接
while (true) {
try {
Socket socket = serverSocket.accept();
// 超过人数处理
if (ServerUI.Clients.size() >= max) {
BufferedReader read = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter write = new PrintWriter(
socket.getOutputStream());
// 接收客户端的用户信息
String userInfo = read.readLine();
StringTokenizer st = new StringTokenizer(userInfo, "@");
User user = new User(st.nextToken(), st.nextToken());
// 反馈连接失败信息
write.println("MAX@亲爱的" + user.getName() + user.getIp()
+ ",服务器在线人数已达上限,请稍后连接!");
write.flush();
// 释放资源
read.close();
write.close();
socket.close();
continue;
}
ServerUI.java
public ServerUI() {
frame = new JFrame("欢迎来到聊天室!");
frame.setIconImage(new ImageIcon("image/icon.png").getImage());// 更改JFrame的图标
showMsg = new JTextArea();
showMsg.setEditable(false);// 显示消息框不可编辑
showMsg.setForeground(Color.red);
userSum = new Label("0 个在线用户");
txt_max = new JTextField("10");// 初始化人数
txt_port = new JTextField("6666");// 初始化端口
txt_msg = new JTextField();
txt_msg.setEnabled(false);
btn_send = new JButton("发送消息");
btn_send.setEnabled(false);
btn_start = new JButton("启动服务器");
btn_stop = new JButton("关闭服务器");
btn_stop.setEnabled(false);
listModel = new DefaultListModel();
userList = new JList(listModel);
northPanel = new JPanel();
northPanel.setLayout(new GridLayout(1, 6));
northPanel.setBorder(new TitledBorder("配置信息"));
northPanel.add(userSum);
northPanel.add(txt_max);
northPanel.add(new JLabel("个用户上限"));
northPanel.add(txt_port);
northPanel.add(new JLabel("端口"));
northPanel.add(btn_start);
northPanel.add(btn_stop);
leftPanel = new JScrollPane(userList);
leftPanel.setBorder(new TitledBorder("在线用户"));
rightPanel = new JScrollPane(showMsg);
rightPanel.setBorder(new TitledBorder("显示客户端消息"));
User.java
public class User {
private String name;
private String ip;
public User(String name, String ip) {
this.name = name;
this.ip = ip;
}
public void setName(String name) {
this.name = name;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getName() {
return name;
}
public String getIp() {
return ip;
}
}
运行结果