java课程设计-多人聊天工具(socket+多线程)
Posted Henrik-Yao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java课程设计-多人聊天工具(socket+多线程)相关的知识,希望对你有一定的参考价值。
大一下学期的java期末课程设计,分享一下
文章目录
课设要求
多人聊天工具
服务器要求1:能够看到所有在线用户(25%)
服务器要求2:能够强制用户下线(25%)
客户端要求1:能够看到所有在线用户(25%)
客户端要求2:能够向某个用户发送消息(25%)
相关知识点
1.服务端能够看到所有在线用户
服务端继承了JFrame,实现可视化,通过socket实现服务端与客户端的连接,服务端每接收一个连接,把传进来的用户名和对应的socket连接封装成一个User对象,把User对象存进一个ArrayList的用户列表并把User对象通过取用户名方法取得用户名存进一个ArrayList的用户名列表,添加一个JPanel组件,将ArrayList中的内容通过循环显示JPanel中并布局在窗体的右边,在每当有人上线或者下线,刷新JPanel组件。
2.服务端能够强制用户下线
创建一个布局在窗体的下方的JPanel,在此JPanel中分别添加JLabel用于显示提示文字,添加JTextField用于获取服务端想要强制用户下线的ID,添加JButton用于绑定强制用户下线的事件监听,事件监听中将获取的JTextField的内容与用户名列表进行逐一匹配,匹配上则创建JSON格式的键值对对象,通过用户列表循环广播告知其他用户,并在用户列表和用户名列表中分别删除该用户信息。
3.客户端能够看到所有在线用户
客户端继承了JFrame,实现可视化,添加了一个布局在窗口右边的JPanel,把从服务端接收到的用户名列表中的信息放进去。
4.客户端要求能够向某个用户发送消息
客户端私发消息通过在消息后面加入-和目标用户名,传给服务端,服务端截取目标用户名,在用户名列表中判断是否存在此人,有则判断是否是私发,私发则向目标用户发送消息,没有则向全部用户发送消息。
5.运用JDBC实现持久化存储用户信息
数据库连接池运用了阿里巴巴的durid,定义一个JDBCUtils类,提供静态代码块加载配置文件,初始化连接池对象,通过Spring框架的JDBCTemplate对象进行sql语句的执行,在UserDao中提供了登录和注册方法,登录方法运用queryForObject方法进行登录查询,如果查到返回一个User对象,查不到则返回空,注册方法直接插入新记录,此处建表语句中把用户名设置成了主键,保证了用户名的唯一性,注册失败有警告弹窗提示。
这里加了一个ChatTest类用于绕过数据库账号校验,可以直接进入客户端进行连接。
6.使用JSONObject对象封装数据
在数据的传输中运用了键值对的形式进行传输,客户端传输给服务端的数据包中,通过判断private键的值来确认是否私发,通过username键告知服务端客户端的用户名,通过msg键传输具体消息,服务端传输给客户端的数据包中,通过判断user_list键的值来确认在线用户及人数
7.使用Maven构建管理项目
项目中运用到了JDBC相关内容和JSONObject对象,导入了一些依赖jar包,其中仓库和配置文件都是用的idea默认配置。
类图
项目框架
核心代码
1.maven配置文件pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>MyChat</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
</dependencies>
</project>
2.服务器端Server.java
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import net.sf.json.JSONObject;
//继承JFrame实现可视化
public class Server extends JFrame{
//用户列表,用于存放连接上的用户信息
ArrayList<User> user_list = new ArrayList<>();
//用户名列表,用于显示已连接上的用户
ArrayList<String> username_list = new ArrayList<>();
//消息显示区域
JTextArea show_area = new JTextArea();
//用户名显示区域
JTextArea show_user = new JTextArea(10, 10);
//socket的数据输出流
DataOutputStream outputStream = null;
//socket的数据输入流
DataInputStream inputStream = null;
//从主函数里面开启服务端
public static void main(String[] args) {
new Server();
}
//构造函数
public Server() {
//设置流式布局
setLayout(new BorderLayout());
//VERTICAL_SCROLLBAR_AS_NEEDED设置垂直滚动条需要时出现
//HORIZONTAL_SCROLLBAR_NEVER设置水平滚动条不出现
//创建信息显示区的画布并添加到show_area
JScrollPane panel = new JScrollPane(show_area,ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
//设置信息显示区标题
panel.setBorder(new TitledBorder("信息显示区"));
//布局到中央
add(panel,BorderLayout.CENTER);
//设置信息显示区为不可编辑
show_area.setEditable(false);
//创建用于显示用户的画布
final JPanel panel_east = new JPanel();
//添加流式布局
panel_east.setLayout(new BorderLayout());
//设置标题
panel_east.setBorder(new TitledBorder("在线用户"));
//在用户显示区添加show_uesr
panel_east.add(new JScrollPane(show_user), BorderLayout.CENTER);
//设置用户显示区域为不可编辑
show_user.setEditable(false);
//将显示用户的画布添加到整体布局的右侧
add(panel_east, BorderLayout.EAST);
//创建关于踢下线用户的画布
final JPanel panel_south = new JPanel();
//创建标签
JLabel label = new JLabel("输入要踢下线用户的ID");
//创建输入框
JTextField out_area = new JTextField(40);
//创建踢下线按钮
JButton out_btn = new JButton("踢下线");
//依次添加进画布
panel_south.add(label);
panel_south.add(out_area);
panel_south.add(out_btn);
//将踢下线用户的画布添加到整体布局的下侧
add(panel_south,BorderLayout.SOUTH);
//设置踢下线按钮的监听
out_btn.addActionListener(e -> {
try {
//用于存储踢下线用户的名字
String out_username;
//从输入框中获取踢下线用户名
out_username = out_area.getText().trim();
//用于判断盖用户是否被踢下线
boolean is_out=false;
//遍历用户列表依次判断
for (int i = 0; i < user_list.size(); i++){
//比较用户名,相同则踢下线
if(user_list.get(i).getUsername().equals(out_username)){
//获取被踢下线用户对象
User out_user = user_list.get(i);
//使用json封装将要传递的数据
JSONObject data = new JSONObject();
//封装全体用户名,广播至所有用户
data.put("user_list", username_list);
//广播的信息内容
data.put("msg", out_user.getUsername() + "被管理员踢出\\n");
//服务端消息显示区显示相应信息
show_area.append(out_user.getUsername() + "被你踢出\\n");
//依次遍历用户列表
for (User value : user_list) {
try {
//获取每个用户列表的socket连接
outputStream = new DataOutputStream(value.getSocket().getOutputStream());
//传递信息
outputStream.writeUTF(data.toString());
} catch (IOException ex) {
ex.printStackTrace();
}
}
//将被踢用户移出用户列表
user_list.remove(i);
//将被踢用户移出用户名列表
username_list.remove(out_user.getUsername());
//刷新在线人数
show_user.setText("人数有 " + username_list.size() + " 人\\n");
//刷新在线用户
for (String s : username_list) {
show_user.append(s + "\\n");
}
//判断踢出成功
is_out=true;
break;
}
}
//根据是否踢出成功弹出相应提示
if(is_out){
JOptionPane.showMessageDialog(null,"踢下线成功","提示",
JOptionPane.WARNING_MESSAGE);
}
if(!is_out){
JOptionPane.showMessageDialog(null,"不存在用户","提示",
JOptionPane.WARNING_MESSAGE);
}
//重置输入框
out_area.setText("");
} catch(Exception ex) {
ex.printStackTrace();
}
});
//设置该窗口名
setTitle("服务器 ");
//引入图片
BufferedImage img;
try {
//根据图片名引入图片
img = ImageIO.read(Server.class.getResource("/a.jpg"));
//设置其为该窗体logo
setIconImage(img);
} catch (IOException exception) {
exception.printStackTrace();
}
//设置窗体大小
setSize(700, 700);
//设置窗体位置可移动
setLocationRelativeTo(null);
//设置窗体关闭方式
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗体可见
setVisible(true);
//socket连接相关代码
try {
//开启socket服务器,绑定端口11111
ServerSocket serverSocket = new ServerSocket(11111);
//信息显示区打印服务器启动时间
show_area.append("服务器启动时间 " + new Date() + "\\n");
//持续接收连接
while (true) {
//接收连接
Socket socket = serverSocket.accept();
//创建用户对象
User user = new User();
//判断是否连接上
if (socket != null) {
//获取输入流
inputStream = new DataInputStream(socket.getInputStream());
//读取输入流
String json = inputStream.readUTF();
//创建信息对象
JSONObject data = JSONObject.fromObject(json);
//信息显示区打印用户上线
show_area.append("用户 " + data.getString("username") + " 在" + new Date() + "登陆系统"+"\\n");
//创建新用户
user = new User();
//存储socket对象
user.setSocket(socket);
//获取输入流用户名
user.setUsername(data.getString("username"));
//添加进用户列表
user_list.add(user);
//添加进用户名列表
username_list.add(data.getString("username"));
//刷新在线人数
show_user.setText("人数有 " + username_list.size() + " 人\\n");
//刷新在线用户
for (String s : username_list) {
show_user.append(s + "\\n");
}
}
//封装信息对象
JSONObject online = new JSONObject();
//设置接收信息对象
online.put("user_list", username_list);
//设置信息内容
online.put("msg", user.getUsername() + "上线了");
//依次遍历,将信息广播给所有在线用户
for (User value : user_list) {
//获取输出流
outputStream = new DataOutputStream(value.getSocket().getOutputStream());
//给所有用户输出上线信息
outputStream.writeUTF(online.toString());
}
//开启新线程,持续接收该socket信息
new Thread(new ServerThread(socket)).start();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
//线程代码
class ServerThread implements Runnable {
//存放全局变量socket
private final Socket socket;
//构造函数,初始化socket
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//获取输入流
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
//持续接收信息
while (true) {
//获取传递进来的信息
String json = inputStream.readUTF();
//封装成json格式
JSONObject data = JSONObject.fromObject(json);
//通过json里面的private判断是否私发
boolean is_private = false;
//私发处理
for (int i = 0; i < user_list.size(); i++) {
//找到私发对象
if (user_list.get(i).getUsername().equals(data以上是关于java课程设计-多人聊天工具(socket+多线程)的主要内容,如果未能解决你的问题,请参考以下文章