client_ui_ChatFrame
package com.chatroom.client.ui;
import javax.swing.*;
import java.net.Socket;
public class ChatFrame extends JFrame {
//聊天面板
private ChatPanel chatPanel;
public ChatFrame(Socket socket, String username, String title) {
super(title);
chatPanel = new ChatPanel(socket,username);
setSize(900,600);
setUndecorated(true);
setContentPane(chatPanel);
setLocationRelativeTo(null);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
//调用聊天面板的start方法,用于接受其他客户端包括自己发送过来的消息,应该是一个并发线程。与自己发信息是异步的
chatPanel.start();
}
// public static void main(String[] args) {
// ChatFrame c = new ChatFrame(null,"聊天窗口");
// }
}
client_ui_ChatPanel
package com.chatroom.client.ui;
import com.chatroom.server.server.ChatServer;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import java.net.Socket;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
public class ChatPanel extends JPanel {
/* 定义Sockt属性 */
private Socket socket;
private String currentUsername;
private JTextArea welcomeArea;
/* 添加面板属性,三个文本框属性 */
private JTextArea showText; //公共聊天显示窗口
private JTextArea countText; //在线人数
private JTextArea nameText;
private JTextArea oneText; //私聊显示窗口
private JTextArea inputText; //输入框
private JTextArea noticeText;
//发送按钮
private JButton sendButton;
//关闭按钮
private JButton closeButton;
public ChatPanel(Socket socket,String currentUsername) {
try {
this.currentUsername = currentUsername;
// 取消面板的默认布局, 使用绝对布局,即使用坐标方式进行布局
this.setLayout(null);
// 修改面板的背景颜色
this.setBackground(new Color(200, 200, 200));
//调用自己封装的初始化布局方式
initLayout();
//初始化TCP协议的客户端
this.socket = socket;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 初始化自己的页面布局:
* 1. 头部布局
* 2. 上半部身体的布局
* 3. 下半部身体的布局
* 4. 聊天输入框的布局
* 5. 两个按钮的布局
*/
public void initLayout(){
headLayout();
body1Layout();
body2Layout();
chatInputArea();
buttonArea();
}
/**
* 两个按钮的布局
*/
private void buttonArea() {
//两个按钮
firstButton();
lastButton();
}
/**
* 第二个按钮"关闭"的布局
*/
private void lastButton() {
closeButton = new JButton("关闭");
closeButton.setBounds(590,550,65,35);
this.add(closeButton);
closeButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int option = JOptionPane.showConfirmDialog(null, "您确定要离开吗");
if (option == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
});
}
/**
* 第一个按钮“发送”的布局
*/
private void firstButton() {
sendButton = new JButton("发送");
sendButton.setBounds(590,505,65,35);
this.add(sendButton);
sendButton.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
String content = inputText.getText().trim();
if (!content.equals("")) {
//发送信息
sendMessage(currentUsername,content);
// 清空文本框的内容
inputText.setText("");
}
}
});
}
private void sendMessage(String currentUsername,String content) {
try {
//获取输出流对象
OutputStream outputStream = socket.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(outputStream, "utf-8"),true);
//封装消息,成Map对象,然后转为JSON发送
Map<String,String> map = new HashMap<>();
map.put("username",currentUsername);
map.put("content",content);
map.put("infoType","2");
System.currentTimeMillis();
map.put("chat_time",System.currentTimeMillis()+"");
ObjectMapper mapper = new ObjectMapper();
String record = mapper.writeValueAsString(map);
pw.println(record);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 聊天窗口的布局
*/
private void chatInputArea() {
//5. 输入窗口
inputText = new JTextArea("输入内容");
//inputText.setBackground(new Color(238,238,238));
inputText.setBounds(5,495,580,100);
this.add(inputText);
}
/**
* 私聊的内容显示窗口,与群公告窗口的布局
*/
private void body2Layout() {
//5. 私聊显示窗口
oneText = new JTextArea("私聊窗口");
oneText.setEditable(false);
oneText.setBackground(new Color(238,238,238));
oneText.setBounds(5,390,650,100);
this.add(oneText);
//6. 公告窗口
noticeText = new JTextArea("公告");
noticeText.setEditable(false);
noticeText.setBackground(new Color(238,238,238));
noticeText.setBounds(660,390,235,205);
this.add(noticeText);
}
/**
* 公共聊天显示窗口和在线成员列表窗口的布局
*/
private void body1Layout() {
//3. 公共聊天组件
showText = new JTextArea();
showText.setBackground(new Color(238,238,238));
showText.setEditable(false);// 设置不可编辑
showText.setBounds(5,30,650,355);
//为showText设置滚动条
JScrollPane scroll = new JScrollPane(showText);
//设置垂直滚动
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
this.add(scroll);
this.add(showText);
//在线成员列表
initPersonnelInfo();
}
//表模型对象属性
private DefaultTableModel personModel;
//表属性
private JTable personTable;
private void initPersonnelInfo() {
//定义一个表头
String[] personTitle = {"在线人名单"};
String[][] personDatas = {};
personModel = new DefaultTableModel(personDatas,personTitle) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
//初始化表对象,与表模型关联
personTable = new JTable(personModel);
personTable.getTableHeader().setFont(new Font("楷体",Font.PLAIN,20));
personTable.getTableHeader().setBackground(Color.LIGHT_GRAY);
personTable.setRowHeight(30);
//模拟多个人在线。 添加行记录
try {
Socket socket = new Socket("localhost",11000);
Runnable task = new GetClientOnlineUsernameHandler(socket);
Thread thread = new Thread(task);
thread.start();
} catch (IOException e) {
System.out.println("监听服务连接失败");
e.printStackTrace();
}
JScrollPane jScrollPane = new JScrollPane();
jScrollPane.setBounds(660,30,235,355);
jScrollPane.setViewportView(personTable);
this.add(jScrollPane);
}
class GetClientOnlineUsernameHandler implements Runnable{
Socket socket;
public GetClientOnlineUsernameHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input,"utf-8"));
String list = "";
while((list = reader.readLine()) != null) {
//清空原有的名单
personModel.setNumRows(0);
//System.out.println("名单:"+list);
String[] usernames = list.split(",");
for (String username : usernames) {
personModel.addRow(new String[]{username});
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 欢迎语,与 人数在线统计的 布局
*/
private void headLayout() {
//0. 欢迎logo
welcomeArea = new JTextArea(currentUsername+"您好,欢迎进入聊天室,找到你喜欢的人,开始聊天吧");
welcomeArea.setBackground(new Color(200, 200, 200));
//不能编辑
welcomeArea.setEditable(false);
welcomeArea.setBounds(5,5,650,20);
this.add(welcomeArea);
//1. 统计人数组件
countText = new JTextArea("在线人数:");
countText.setEditable(false);
countText.setBackground(new Color(200,200,200));
countText.setBounds(660,5,235,20);
this.add(countText);
}
public void start(){
try{
//获取一个获取服务端信息的处理器任务
Runnable task = new GetServerInfoHandler();
Thread thread = new Thread(task);
thread.start();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 编写一个获取服务端信息的处理器。即一个任务体
*/
class GetServerInfoHandler implements Runnable{
public void run(){
try {
//获取接受服务端发送过来的数据的输入流对象
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream,"utf-8"));
String info = "";
while((info = br.readLine()) != null){
ObjectMapper mapper = new ObjectMapper();
Map<String,String> record = mapper.readValue(info, Map.class);
String username = record.get("username");
long chatTime = Long.parseLong(record.get("chat_time"));
Timestamp timestamp = new Timestamp(chatTime);
showText.append(timestamp+"\r\n");
if(username.equals(currentUsername)){
showText.append("我说:\r\n");
}else {
showText.append(username+"说:\r\n");
}
showText.append(" "+record.get("content")+"\r\n\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void flushOnLineList(String names) {
//更新在线人员名称列表
String[] nameArray = names.split(":");
nameText.setText("");
for (String s : nameArray) {
nameText.append(s+"\r\n");
}
}
}
client_ui_LoginFrame
package com.chatroom.client.ui;
import com.chatroom.client.util.SocketUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* 登录窗口: 继承自JFrame 窗口
*/
public class LoginFrame extends JFrame{
private Socket socket;
//提供两个输入框
private JTextField userText; //用于输入用户名
private JTextField passwordText; //用于输入密码
// 字体 样式 字号
Font font = new Font("幼圆",Font.BOLD,28);
public LoginFrame() {
//初始化用户名部分
initUsername();
//初始化密码部分
initPassword();
//初始化两个按钮
initButton();
//当前窗口的其他设置
initFrame();
//初始化
try {
socket =new Socket("localhost",7777);
} catch (IOException e) {
e.printStackTrace();
}
}
private void initUsername() {
//定义标签
JLabel userLabel = new JLabel("用户名:");
userLabel.setFont(font);
//设置标签的位置信息: 左上角的xy坐标以及宽和高
userLabel.setBounds(200, 200, 100, 40);
//获取窗口的面板,然后将标签添加到面板上
this.getContentPane().add(userLabel);
userText = new JTextField();
userText.setBounds(350, 200, 250, 40);
userText.setFont(font);
this.getContentPane().add(userText);
}
private void initPassword() {
JLabel passwordLabel = new JLabel("密 码:");
passwordLabel.setFont(font);
passwordLabel.setBounds(200, 280, 100, 40);
this.getContentPane().add(passwordLabel);
passwordText = new JTextField();
passwordText.setBounds(350, 280, 250, 40);
passwordText.setFont(font);
this.getContentPane().add(passwordText);
}
private void initButton() {
//定义一个按钮对象
JButton logoutButton = new JButton("退出");
logoutButton.setFont(font);
//按钮的美化
logoutButton.setBorder(BorderFactory.createRaisedBevelBorder());
logoutButton.setFocusPainted(false);
//按钮的位置
logoutButton.setBounds(200,350,100,40);
//给按钮添加监听事件
logoutButton.addActionListener(new ActionListener() {
/**
* 执行逻辑
* @param e
*/
@Override
public void actionPerformed(ActionEvent e) {
//定义一个确认框
int option = JOptionPane.showConfirmDialog(null, "您确定要离开吗");
if (option == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
});
//将登出按钮添加到面板上
this.getContentPane().add(logoutButton);
JButton loginButton = new JButton("登录");
loginButton.setFont(font);
loginButton.setBorder(BorderFactory.createRaisedBevelBorder());
loginButton.setFocusPainted(false);
loginButton.setBounds(350,350,100,40);
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//应该获取用户名和密码,提交到服务端
String username = userText.getText().trim();
String password = passwordText.getText().trim();
sendDataToServer(username,password);
}
});
this.getContentPane().add(loginButton);
JButton toRegButton = new JButton("注册");
toRegButton.setFont(font);
toRegButton.setBorder(BorderFactory.createRaisedBevelBorder());
toRegButton.setFocusPainted(false);
toRegButton.setBounds(500,350,100,40);
toRegButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//点击注册,应该跳转到注册页面,new一个注册创建即可。
new RegisterFrame();
//释放当前窗口
LoginFrame.this.dispose();
}
});
this.getContentPane().add(toRegButton);
}
protected void sendDataToServer(String username,String password) {
if(username==null||username.length()==0) {
JOptionPane.showMessageDialog(null, "用户名不能为空");
return;
}
if(password==null||password.length()==0) {
JOptionPane.showMessageDialog(null, "密码不能为空");
return;
}
try {
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(
output,"utf-8"),true);
Map<String,Object> map = new HashMap<>();
map.put("username",username);
map.put("password",password);
map.put("infoType","1");
ObjectMapper mapper = new ObjectMapper();
String jsonStr = mapper.writeValueAsString(map);
writer.println(jsonStr);
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
boolean result = Boolean.parseBoolean(br.readLine());
if(result){
JOptionPane.showMessageDialog(null,"登录成功..准备跳转到聊天页面...");
this.dispose();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 登录成功,登录的socket对象,不应该下线,聊天窗口继续使用。
* 因此可以作为聊天窗口的实参传入到聊天窗口中,继续使用。
* 登录成功的用户名,也可以传入聊天窗口里继续使用。
*/
new ChatFrame(socket,username,"聊天窗口");
}else{
JOptionPane.showMessageDialog(null,"登录失败,用户名不存在,或者密码错误");
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void initFrame() {
this.getContentPane().setFont(font);
this.setSize(900,600);
//取消窗口的边框
this.setUndecorated(true);
//窗口居中
this.setLocationRelativeTo(null);
//点击窗口的x,表示退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//取消面板的默认布局
this.getContentPane().setLayout(null);
//设置窗口的可见性
this.setVisible(true);
}
public static void main(String[] args) {
new LoginFrame();
}
}
client_ui_RegisterFrame
package com.chatroom.client.ui;
import com.chatroom.client.util.SocketUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class RegisterFrame extends JFrame {
private JTextField userText;
private JTextField passwdText;
private JTextField phoneText;
//性别的单选框,
private JRadioButton s1;
private JRadioButton s2;
private Font font = new Font("幼圆",Font.BOLD,30);
//定义一个面板属性, 并不像登录窗口那样从this身上获取的。
private JPanel mainPanel;
//定义客户端套接字属性
private Socket socket = null;
public RegisterFrame() {
initMainPanel();
initLabel();
initUserPanel();
initPasswdPanel();
initPhonePanel();
initSexPanel();
initButtonPanel();
initFrame();
//套接字初始化
try {
socket = new Socket("127.0.0.1",7777);
} catch (IOException e) {
e.printStackTrace();
}
}
private void initFrame() {
this.setSize(900, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//false 表示不可以改变窗口的尺寸
this.setResizable(false);
this.setLocationRelativeTo(null);
// this.setUndecorated(true);
this.setVisible(true);
}
//初始化窗口的面板
public void initMainPanel() {
//创建面板对象
mainPanel = new JPanel();
//设置了面板的布局 6行 1列 水平间隔 垂直间隔
mainPanel.setLayout(new GridLayout(6, 1, 0, 0));
//将面板添加到窗口上
this.add(mainPanel);
}
//设置第一行的布局
public void initLabel() {
//定义一个标签面板 边框布局:上下左右中
JPanel labelPanel = new JPanel(new BorderLayout());
mainPanel.add(labelPanel);
JLabel label = new JLabel("用户注册",JLabel.CENTER);
label.setFont(font);
//将标签添加到标签面板上
labelPanel.add(label);
}
private void initUserPanel() {
JPanel userPanel = new JPanel();
mainPanel.add(userPanel);
JLabel userLabel = new JLabel("用户名:");
userLabel.setFont(font);
userPanel.add(userLabel);
userText = new JTextField();
userText.setColumns(15);
userText.setFont(font);
userPanel.add(userText);
}
public void initPasswdPanel() {
JPanel passwdPanel = new JPanel();
mainPanel.add(passwdPanel);
JLabel passwdLabel = new JLabel("密 码:");
passwdLabel.setFont(font);
passwdPanel.add(passwdLabel);
passwdText = new JTextField();
passwdText.setFont(font);
passwdText.setColumns(15);
passwdPanel.add(passwdText);
}
public void initPhonePanel() {
JPanel phonePanel = new JPanel();
mainPanel.add(phonePanel);
JLabel phoneLabel = new JLabel("手机号:");
phoneLabel.setFont(font);
phonePanel.add(phoneLabel);
phoneText = new JTextField();
phoneText.setColumns(15);
phoneText.setFont(font);
phonePanel.add(phoneText);
}
public void initSexPanel() {
JPanel panel = new JPanel();
mainPanel.add(panel);
JLabel sexLabel = new JLabel("性 别: ");
sexLabel.setFont(font);
sexLabel.setBounds(50, 5, 50, 50);
s1 = new JRadioButton("男",true);
s2 = new JRadioButton("女");
s1.setFont(font);
s2.setFont(font);
//将两个单选框定义为一组
ButtonGroup bg = new ButtonGroup();
bg.add(s1);
bg.add(s2);
//将标签和单选框添加到性别面板上
panel.add(sexLabel);
panel.add(s1);
panel.add(s2);
}
public void initButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
mainPanel.add(panel);
Font font = new Font("宋体",Font.PLAIN,30);
JButton registButton = new JButton("注册");
registButton.setPreferredSize(new Dimension(100, 50));
registButton.setFont(font);
//凸起
registButton.setBorder(BorderFactory.createRaisedBevelBorder());
registButton.setFocusPainted(false);
//给注册按钮添加监听事件
registButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//将注册信息收集起来
String info = getRegistInfo();
if(info!=null){
sendRegistInfoToServer(info);
}
}
});
JButton toLoginButton = new JButton("前往登录界面");
toLoginButton.setPreferredSize(new Dimension(200, 50));
toLoginButton.setFont(font);
toLoginButton.setBorder(BorderFactory.createRaisedBevelBorder());
toLoginButton.setFocusPainted(false);
//监听
toLoginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new LoginFrame();
//销毁当前窗口
RegisterFrame.this.dispose();
}
});
panel.add(registButton);
panel.add(toLoginButton);
}
private void sendRegistInfoToServer(String info) {
try {
OutputStream output = socket.getOutputStream();
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(
output,"utf-8"),true);
pw.println(info);
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
boolean result = Boolean.parseBoolean(br.readLine());
if(result){
JOptionPane.showMessageDialog(null,"注册成功");
}else{
JOptionPane.showMessageDialog(null,"注册失败");
}
} catch (IOException e) {
e.printStackTrace();
}
}
protected String getRegistInfo() {
String json = null;
try {
String username = userText.getText().trim();
if(username==null||username.length()==0) {
JOptionPane.showMessageDialog(null, "用户名不能为空");
return null;
}
String passwd = passwdText.getText().trim();
if(passwd==null||passwd.length()==0) {
JOptionPane.showMessageDialog(null, "密码不能为空");
return null;
}
String phone = phoneText.getText().trim();
if(phone==null||phone.length()==0) {
JOptionPane.showMessageDialog(null, "手机号不能为空");
return null;
}
String sex = null;
if(s1.isSelected()) {
sex = s1.getText();
}else {
sex = s2.getText();
}
//将四个值封装成Map对象
Map<String,Object> map = new HashMap<>();
map.put("username",username);
map.put("password",passwd);
map.put("telephone",phone);
map.put("gender",sex);
map.put("infoType","0");// 0 注册信息, 1 登录信息 2 公聊 3 私聊
// 创建ObjectMapper实例
ObjectMapper mapper = new ObjectMapper();
// 将Map转换为JSON字符串
json = mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
// 打印JSON字符串
System.out.println(json);
return json;
}
public static void main(String[] args) {
new RegisterFrame().setVisible(true);
}
}
client_util_SocketUtil
package com.chatroom.client.util;
import java.io.IOException;
import java.net.Socket;
/**
* 客户端获取Socket的工具类
* 1. 设计成单例模式。 客户端只能单开
*/
public class SocketUtil {
private static Socket socket;
private SocketUtil(){}
public synchronized static Socket getSocketInstance(){
if(socket == null){
try {
socket = new Socket("127.0.0.1",7777);
} catch (IOException e) {
e.printStackTrace();
}
}
return socket;
}
}
server_dao_impl_ChatRecordDaoImpl
package com.chatroom.server.dao.impl;
import com.chatroom.server.dao.ChatRecordDao;
import com.chatroom.server.util.DruidUtil;
import com.chatroom.server.vo.ChatRecord;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class ChatRecordDaoImpl implements ChatRecordDao {
@Override
public void addChatRecord(ChatRecord chatRecord) {
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "insert into chat_record values (null,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, chatRecord.getUsername());
ps.setString(2,chatRecord.getContent());
ps.setTimestamp(3,chatRecord.getChattime());
ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
}
}
server_dao_impl_UserDaoImpl
package com.chatroom.server.dao.impl;
import com.chatroom.server.dao.UserDao;
import com.chatroom.server.util.DruidUtil;
import com.chatroom.server.vo.User;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class UserDaoImpl implements UserDao {
@Override
public boolean addUser(User user) {
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "insert into userinfo values(null,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1,user.getUsername());
ps.setString(2,user.getPassword());
ps.setString(3,user.getTelephone());
ps.setString(4,user.getGender());
ps.executeUpdate();
return true;
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
return false;
}
@Override
public boolean checkLogin(User user) {
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "select username,password,telephone,gender from userinfo where username=? and password=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1,user.getUsername());
ps.setString(2,user.getPassword());
ResultSet set = ps.executeQuery();
if(set.next()){
return true;
}
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
return false;
}
}
server_dao_impl_UserOfOnlineDaoImpl
package com.chatroom.server.dao.impl;
import com.chatroom.server.dao.UserOfOnlineDao;
import com.chatroom.server.util.DruidUtil;
import com.chatroom.server.vo.UserOfOnline;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class UserOfOnlineDaoImpl implements UserOfOnlineDao {
@Override
public UserOfOnline findUserOfOnlineByUsername(String username) {
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "select * from user_online where username=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
ResultSet rs = ps.executeQuery();
if(rs.next()){
UserOfOnline user = new UserOfOnline();
user.setUsername(rs.getString("username"));
user.setLoginTime(rs.getTimestamp("login_time"));
user.setLogoutTime(rs.getTimestamp("logout_time"));
user.setStatus(rs.getString("status"));
return user;
}
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
return null;
}
@Override
public void addUserOfOnline(String username) {
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "insert into user_online values(null,?,now(),null,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, "1");
ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
}
@Override
public void changeToOnline(String username) {
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "update user_online set status=?,login_time = now() where username=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "1");
ps.setString(2,username);
ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
}
@Override
public void changeToOffline(String username) {
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "update user_online set status=?,logout_time = now() where username=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "0");
ps.setString(2,username);
ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
}
@Override
public List<String> findAllOnlineUsername() {
List<String> usernames = new ArrayList<>();
Connection conn = null;
try{
conn = DruidUtil.getConnection();
String sql = "select username from user_online where status=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "1");
ResultSet rs = ps.executeQuery();
while(rs.next()){
String username = rs.getString("username");
usernames.add(username);
}
}catch (Exception e){
e.printStackTrace();
}finally {
DruidUtil.closeConnection(conn);
}
return usernames;
}
}
server_dao_ChatRecordDao
package com.chatroom.server.dao;
import com.chatroom.server.vo.ChatRecord;
public interface ChatRecordDao {
void addChatRecord(ChatRecord chatRecord);
}
server_dao_UserDao
package com.chatroom.server.dao;
import com.chatroom.server.vo.User;
public interface UserDao {
boolean addUser(User user);
boolean checkLogin(User user);
}
server_dao_UserOfOnlineDao
package com.chatroom.server.dao;
import com.chatroom.server.vo.UserOfOnline;
import java.util.List;
public interface UserOfOnlineDao {
UserOfOnline findUserOfOnlineByUsername(String username);
void addUserOfOnline(String username);
void changeToOnline(String username);
void changeToOffline(String username);
List<String> findAllOnlineUsername();
}
server_server_ChatServer
package com.chatroom.server.server;
import com.chatroom.server.dao.ChatRecordDao;
import com.chatroom.server.dao.UserDao;
import com.chatroom.server.dao.UserOfOnlineDao;
import com.chatroom.server.util.DaoFactory;
import com.chatroom.server.util.InfoUtil;
import com.chatroom.server.vo.ChatRecord;
import com.chatroom.server.vo.User;
import com.chatroom.server.vo.UserOfOnline;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
/**
* 聊天服务器
*/
public class ChatServer {
//定义一个TCP通信协议的服务端属性
private ServerSocket server;
//添加一个Map属性,用于存储多个客户端的用户名与输出流对象。 Map集合(散列表)
private Map<String, PrintWriter> allOut;
private ServerSocket server2;
public ChatServer(int port) {
try {
server = new ServerSocket(port);
//给map集合赋值
allOut = new HashMap<String, PrintWriter>();
//监听端口
server2 = new ServerSocket(11000);
} catch (IOException e) {
System.out.println("---服务器启动失败---");
}
}
public void listenerOnlineUsername() {
try {
while (true) {
System.out.println("---监听客户端的上线状态---");
Socket socket = server2.accept();
System.out.println("---一个客户端正在被监听---");
//每获取一个客户端的Socket对象,就应该将其放入一个并发线程中
Runnable task = new GetOnlineUsernameHandler(socket);
Thread thread = new Thread(task);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
class GetOnlineUsernameHandler implements Runnable {
private Socket socket;
public GetOnlineUsernameHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
UserOfOnlineDao userOfOnlineDao = DaoFactory.getUserOfOnlineDao();
List<String> allOnlineUsername = userOfOnlineDao.findAllOnlineUsername();
StringBuilder builder = new StringBuilder();
for (String username : allOnlineUsername) {
builder.append(username).append(",");
}
OutputStream outputStream = socket.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(outputStream), true);
pw.println(builder.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}, 1000,1000);
}
}
/**
* 聊天的主方法
*/
public void start() {
try {
while (true) {
System.out.println("---等待客户端连接---");
Socket socket = server.accept();
System.out.println("---一个客户端连接上了---");
//每获取一个客户端的Socket对象,就应该将其放入一个并发线程中
Runnable task = new GetClientInfoHandler(socket);
Thread thread = new Thread(task);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//创建一个具体的服务器对象
ChatServer chatServer = new ChatServer(7777);
Runnable runnable = () -> {
//调用聊天的主方法
chatServer.start();
};
Runnable runnable2 = () -> {
chatServer.listenerOnlineUsername();
};
Thread thread = new Thread(runnable);
Thread thread2 = new Thread(runnable2);
thread.start();
thread2.start();
}
/**
* 定义一个处理客户端信息的处理器,即一个任务体
*/
class GetClientInfoHandler implements Runnable {
private String nickname;
//因为run方法中使用了 start方法中的socket局部变量
//所以可以在该类中添加一个属性,run方法中可以访问该类的属性
private Socket socket;
//提供一个构造器,将start方法中的局部变量socket传进来,给属性赋值, 这样,run方法中使用的socket就是
//start方法中的局部变量指向的对象
public GetClientInfoHandler(Socket socket) {
this.nickname = nickname;
this.socket = socket;
}
@Override
public void run() {
PrintWriter pw = null;
try {
//通过服务端的Socket对象,获取输入流,接受客户端发送过来的数据
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream, "utf-8"));
//通过服务端的Socket对象,获取输出流,将信息返回给客户端
OutputStream outputStream = socket.getOutputStream();
pw = new PrintWriter(
new OutputStreamWriter(outputStream, "utf-8"), true);
//上线通知
//onlineNotice(nickname,pw);
//循环处理客户端发送过来的信息
String info = "";
while ((info = br.readLine()) != null) {
//解析发送过来的信息:
String infoType = InfoUtil.getInfoType(info);
System.out.println("infoType:" + infoType);
if (infoType.equals("0")) { //注册
User user = InfoUtil.getUserFromInfo(info);
UserDao userDao = DaoFactory.getUserDao();
boolean flag = userDao.addUser(user);
//告诉注册界面
pw.println(flag);
System.out.println("是否注册成功:" + flag);
} else if (infoType.equals("1")) {//登录
User user = InfoUtil.getUserFromInfo(info);
UserDao userDao = DaoFactory.getUserDao();
boolean flag = userDao.checkLogin(user);
//告诉登录界面
pw.println(flag);
System.out.println("是否登录成功:" + flag);
//登录成功,将输出流添加到Map集合中
allOut.put(user.getUsername(), pw);
//修改状态
UserOfOnlineDao userOfOnlineDao = DaoFactory.getUserOfOnlineDao();
UserOfOnline userOfOnline = userOfOnlineDao.findUserOfOnlineByUsername(user.getUsername());
if (userOfOnline != null) {
//不是第一次登录,改为上线
userOfOnlineDao.changeToOnline(user.getUsername());
} else {
//第一次登录
userOfOnlineDao.addUserOfOnline(user.getUsername());
}
} else if (infoType.equals("2")) { //公开聊天
ChatRecord record = InfoUtil.getChatRecordFromInfo(info);
ChatRecordDao recordDao = DaoFactory.getChatRecordDao();
recordDao.addChatRecord(record);
sendToAllClient(info);
} else {//私聊
}
}
} catch (IOException e) {
System.out.println("---一个客户端离线了---");
offlineNotice(nickname, pw);
}
}
}
private void onlineNotice(String nickname, PrintWriter pw) {
//给自己发送信息
pw.println(nickname);
Set<String> names = allOut.keySet();
//更新在线人数列表
StringBuilder sBuilder = new StringBuilder();
for (String name : names) {
sBuilder.append(name).append(":");
}
System.out.println("名字拼接:" + sBuilder.toString());
}
private void offlineNotice(String nickname, PrintWriter pw) {
}
public void sendToOneClient(String nickname, String info) {
//获取要私聊的对象名称
//获取第一个冒号的下标
int index = info.indexOf(":");
String toNickName = info.substring(1, index);
//找到对应的流对象
PrintWriter printWriter = allOut.get(toNickName);
printWriter.println(nickname + "对你说:" + info.substring(index + 1));
}
public void sendToAllClient(String info) {
//遍历Map里的所有输出流,来发送info信息
Collection<PrintWriter> pws = allOut.values();
System.out.println("---sendToAllClient---");
for (PrintWriter pw : pws) {
System.out.println("---进入循环----");
pw.println(info);
}
}
}
server_util_DaoFactory
package com.chatroom.server.util;
import com.chatroom.server.dao.ChatRecordDao;
import com.chatroom.server.dao.UserDao;
import com.chatroom.server.dao.UserOfOnlineDao;
import com.chatroom.server.dao.impl.ChatRecordDaoImpl;
import com.chatroom.server.dao.impl.UserDaoImpl;
import com.chatroom.server.dao.impl.UserOfOnlineDaoImpl;
public class DaoFactory {
private static UserDao userDao;
private static ChatRecordDao chatRecordDao;
private static UserOfOnlineDao userOfOnlineDao;
private DaoFactory(){}
public static UserDao getUserDao() {
if (userDao == null) {
userDao = new UserDaoImpl();
}
return userDao;
}
public static ChatRecordDao getChatRecordDao() {
if (chatRecordDao == null) {
chatRecordDao = new ChatRecordDaoImpl();
}
return chatRecordDao;
}
public static UserOfOnlineDao getUserOfOnlineDao() {
if (userOfOnlineDao == null) {
userOfOnlineDao = new UserOfOnlineDaoImpl();
}
return userOfOnlineDao;
}
}
server_util_DruidUtil
package com.chatroom.server.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DruidUtil {
//定义一个静态变量:关于Druid的连接池的
private static DataSource ds;
static{
try {
//先自己读取配置文件,封装到Properties对象里
InputStream io = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
properties.load(io);
//Druid提供了一个工厂类,里面提供了一个createDataSource(Properties prop)方法
// 会自动解析prop里的各种键值对,进行赋值
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() {
Connection conn = null;
try{
conn = ds.getConnection();
return conn;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public static void closeConnection(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Connection conn = getConnection();
System.out.println(conn);
closeConnection(conn);
}
}
server_util_InfoUtil
package com.chatroom.server.util;
import com.chatroom.server.vo.ChatRecord;
import com.chatroom.server.vo.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.sql.Timestamp;
import java.util.Map;
public class InfoUtil {
/**
* 将json格式的字符串转成Map对象
* @param info
* @return
*/
public static Map<String,String> parseJsonToMap(String info){
try {
ObjectMapper mapper = new ObjectMapper();
//调用readValue(String jsonStr,JavaClass class)
//jsonStr: 指的就是json格式的字符串
//class: 要转成的java类型的描述类对象
Map<String,String> map = mapper.readValue(info,Map.class);
//System.out.println("map:"+map.toString());
return map;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取信息的类型
* @param info
* @return
*/
public static String getInfoType(String info){
Map<String, String> map = parseJsonToMap(info);
return map.get("infoType");
}
public static User getUserFromInfo(String info){
Map<String, String> map = parseJsonToMap(info);
User user = new User();
user.setUsername(map.get("username"));
user.setPassword(map.get("password"));
user.setTelephone(map.get("telephone"));
user.setGender(map.get("gender"));
return user;
}
public static ChatRecord getChatRecordFromInfo(String info){
Map<String, String> map = parseJsonToMap(info);
ChatRecord record = new ChatRecord();
record.setUsername(map.get("username"));
record.setContent(map.get("content"));
long timeString = Long.parseLong(map.get("chat_time"));
Timestamp timestamp = new Timestamp(timeString);
record.setChattime(timestamp);
return record;
}
}
server_vo_ChatRecord
package com.chatroom.server.vo;
import java.sql.Timestamp;
import java.util.Objects;
public class ChatRecord {
private int id;
private String username;
private String content;
private Timestamp chattime;
public ChatRecord() {}
public ChatRecord(int id, String username, String content, Timestamp chattime) {
this.id = id;
this.username = username;
this.content = content;
this.chattime = chattime;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Timestamp getChattime() {
return chattime;
}
public void setChattime(Timestamp chattime) {
this.chattime = chattime;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ChatRecord that = (ChatRecord) o;
return id == that.id && Objects.equals(username, that.username) && Objects.equals(content, that.content) && Objects.equals(chattime, that.chattime);
}
@Override
public int hashCode() {
return Objects.hash(id, username, content, chattime);
}
@Override
public String toString() {
return "ChatRecord{" +
"id=" + id +
", username='" + username + '\'' +
", content='" + content + '\'' +
", chattime='" + chattime + '\'' +
'}';
}
}
server_vo_User
package com.chatroom.server.vo;
import java.util.Objects;
public class User {
private Integer id;
private String username;
private String password;
private String telephone;
private String gender;
public User(){}
public User(Integer id, String username, String password, String telephone, String gender) {
this.id = id;
this.username = username;
this.password = password;
this.telephone = telephone;
this.gender = gender;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id == user.id && Objects.equals(username, user.username) && Objects.equals(password, user.password) && Objects.equals(telephone, user.telephone) && Objects.equals(gender, user.gender);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password, telephone, gender);
}
public String toString(){
return id + "," + username + "," + password + "," + telephone + "," + gender;
}
}
server_vo_UserOfOnline
package com.chatroom.server.vo;
import java.sql.Timestamp;
import java.util.Objects;
public class UserOfOnline {
private Integer id;
private String username;
private Timestamp loginTime;
private Timestamp logoutTime;
private String status;
public UserOfOnline() {
}
public UserOfOnline(Integer id, String username, Timestamp loginTime, Timestamp logoutTime, String status) {
this.id = id;
this.username = username;
this.loginTime = loginTime;
this.logoutTime = logoutTime;
this.status = status;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Timestamp getLoginTime() {
return loginTime;
}
public void setLoginTime(Timestamp loginTime) {
this.loginTime = loginTime;
}
public Timestamp getLogoutTime() {
return logoutTime;
}
public void setLogoutTime(Timestamp logoutTime) {
this.logoutTime = logoutTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserOfOnline that = (UserOfOnline) o;
return Objects.equals(id, that.id) && Objects.equals(username, that.username) && Objects.equals(loginTime, that.loginTime) && Objects.equals(logoutTime, that.logoutTime) && Objects.equals(status, that.status);
}
@Override
public int hashCode() {
return Objects.hash(id, username, loginTime, logoutTime, status);
}
@Override
public String toString() {
return "UserOnline{" +
"id=" + id +
", username='" + username + '\'' +
", loginTime=" + loginTime +
", logoutTime=" + logoutTime +
", status='" + status + '\'' +
'}';
}
}
druid.properties
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/chat_db?serverTimezone=Asia/Shanghai&useTimezone=true
username=root
password=111111
initialSize=5
maxActive=50
maxIdle=10
minIdle=3
maxWait=60000