代码的实现
别忘了引入驱动
Client
package chat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import com.mysql.cj.x.protobuf.MysqlxDatatypes.Array;
public class Client extends JFrame implements ActionListener {
JComboBox cmb;
JTextArea jta2, jta1, jt;
Socket socket;
String getname;
String currentchatname;
String getContent;
byte[] b;
String title;
JLabel label1;
String getAllUser[];
String get = "";
private static final int MESSAGE_SIZE = 1024;
public Client() {
createMidPanel();
createSouthPanel();
createNorthPanel();
initFrame();
try {
socket = new Socket("127.0.0.1", 45678);
if (socket.isConnected() == true) {
System.out.println("连接成功");
new Thread() {// 开启一个接受数据的线程
@Override
public void run() {
super.run();
b = new byte[MESSAGE_SIZE];
try {
InputStream in = socket.getInputStream();
while (true) {
Tools.resetArray(b);
in.read(b);
if (new String(b).contains("客户")) {
int usernum = Tools.getNum(new String(b).trim());
title = "用户" + usernum;
jt.setText(title);
} else {
jta1.append(new String(b).trim() + "\n");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void createNorthPanel() {
JPanel north = new JPanel(new BorderLayout());
label1 = new JLabel();
jt = new JTextArea(1, 10);
jt.setEditable(false); // 设置文本不可编辑
jt.setFont(new Font("楷体", Font.BOLD, 20));
jt.setBackground(new Color(241, 243, 244));
jt.setForeground(new Color(143, 140, 138));
label1.setForeground(new Color(143, 140, 138));
north.add(jt, BorderLayout.WEST);
north.add(label1, BorderLayout.CENTER);
this.add(north, BorderLayout.NORTH);
}
private void createMidPanel() {
JPanel midPanel = new JPanel();
JPanel midPanel1 = new JPanel(new GridLayout(10, 1));
jta1 = new JTextArea(7, 1);
JScrollPane js1 = new JScrollPane(jta1);
ResultSet result;
try {
result = new Tools().getData();
while (result.next()) {
get = get + result.getString(1) + " ";
}
String splict[] = get.split(" ");
JButton ta[] = new JButton[splict.length];
for (int i = 0; i < splict.length; i++) {
ta[i] = new JButton(splict[i]);
ta[i].setBackground(new Color(63, 174, 225));
ta[i].setForeground(Color.white);
if (splict[i].equals(title)) {
ta[i].setEnabled(true);
}
ta[i].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
label1.setText("当前正与:" + e.getActionCommand() + "联系");
currentchatname = e.getActionCommand();
try {
ResultSet set = new Tools().getData1(title, e.getActionCommand());
jta1.setText("");
while (set.next()) {
if (set.getString(3).equals("发")) {
jta1.append("我:" + set.getString(4) + "\n"+set.getString(5)+"\n");
} else {
jta1.append(e.getActionCommand() + ":" + set.getString(4) + "\n"+ set.getString(5)+"\n");
}
}
} catch (SQLException e1) {
e1.printStackTrace();
}
}
});
midPanel1.add(ta[i]);
}
} catch (SQLException e) {
e.printStackTrace();
}
jta1.setLineWrap(true);// 自动换行
jta1.setEditable(false); // 设置文本不可编辑
jta1.setFont(new Font("楷体", Font.BOLD, 20));
jta1.setBackground(new Color(152, 201, 238));
jta1.setForeground(Color.white);
jta2 = new JTextArea(5, 1);// 发送
JScrollPane js2 = new JScrollPane(jta2);
jta2.setLineWrap(true);// 自动换行
// 在添加组建之前,更改布局
midPanel.setLayout(new BorderLayout());
midPanel.add(js1, BorderLayout.CENTER);
midPanel.add(js2, BorderLayout.SOUTH);
this.add(midPanel, BorderLayout.CENTER);
this.add(midPanel1, BorderLayout.WEST);
}
private void createSouthPanel() {
JPanel southPanel = new JPanel();// 创建一个面板对象
JTextField southIP = new JTextField(10);
southIP.setText("127.0.0.1");
JButton jb1 = new JButton("发送");
jb1.setBackground(new Color(40, 162, 223));
jb1.setForeground(Color.white);
JButton offline = new JButton("下线");
offline.setBackground(new Color(40, 162, 223));
offline.setForeground(Color.white);
jb1.addActionListener(this);
offline.addActionListener(this);
southPanel.add(jb1);
southPanel.add(offline);
// 将面板添加到JFrame
this.add(southPanel, BorderLayout.SOUTH);
}
private void initFrame() {
ImageIcon titltIcon = new ImageIcon("C:\\Users\\DELL\\Desktop\\QQ.png");
this.setIconImage(titltIcon.getImage());
this.setTitle("聊天程序");
this.pack(); // 刷新窗口组建
this.setSize(600, 650);
this.setBackground(new Color(152, 201, 238));
this.setLocation(200, 50);
this.setResizable(false);
this.setVisible(true);// 设置窗体可见性
}
@Override
public void actionPerformed(ActionEvent e) {
OutputStream out = null;
if (e.getActionCommand().equals("发送")) {
getname = currentchatname;
getContent = jta2.getText();
if (getContent.equals("")) {
JOptionPane.showMessageDialog(null, "不能发送空白内容");
return;
}
String send = Tools.getNum(getname) + " " + getContent;
jta1.append("我" + ":" + getContent + "\n");
jta2.setText("");
try {
new Tools().insertData(title, "发", getContent, currentchatname);
new Tools().insertData(currentchatname, "接", getContent, title);
out = socket.getOutputStream();
out.write(send.getBytes());
out.flush();
} catch (IOException | SQLException e1) {
e1.printStackTrace();
}
} else if (e.getActionCommand().equals("下线")) {
try {
out = socket.getOutputStream();
out.write("end".getBytes());
out.flush();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.exit(0);
return;
}
}
public static void main(String[] args) {
// TODO自动生成的方法存根
new Client();
}
}
Server
package chat;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
List<ReceiveThread> receiveList = new ArrayList<>();// 存放已连接客户端类
private final static int MESSAGE_SIZE = 1024;// 每次允许接受数据的最大长度
int num = 0;// 客户端编号
public static void main(String[] args) {
new Server();
}
// 服务端处理逻辑
Server() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(45678);// 用来监听的套接字,指定端口号
while (true) {
Socket socket = serverSocket.accept();// 监听客户端连接,阻塞线程
System.out.println("连接上客户端:" + num);
// 在其他线程处理接收来自客户端的消息
ReceiveThread receiveThread = new ReceiveThread(socket, num);
receiveThread.start();
receiveList.add(receiveThread);
// 有客户端新上线,服务器就通知其他客户端
String notice = "有新客户端上线,现在在线客户端有:客户端:";
for (ReceiveThread thread : receiveList) {
notice = notice + "" + thread.num;
}
for (ReceiveThread thread : receiveList) {
// new SendThread(thread.socket, notice).start();
new SendThread(thread.socket, "").start();
}
num++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 接受消息的线程(同时也有记录对应客户端socket的作用)
class ReceiveThread extends Thread {
int num;
Socket socket;// 客户端对应的套接字
boolean continueReceive = true;// 标识是否还维持连接需要接收
public ReceiveThread(Socket socket, int num) {
this.socket = socket;
this.num = num;
try {
// 给连接上的客户端发送,分配的客户端编号的通知
socket.getOutputStream().write(("客户" + num).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
super.run();
// 接收客户端发送的消息
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] b;
while (continueReceive) {
b = new byte[MESSAGE_SIZE];
inputStream.read(b);
b = Tools.splitByte(b);// 去掉数组无用部分
// 发送end的客户端断开连接
if (new String(b).equals("end")) {
continueReceive = false;
receiveList.remove(this);
// 通知其他客户端
String message = "客户" + num + "连接断开\n" + "现在在线的有,客户端:";
for (ReceiveThread receiveThread : receiveList) {
message = message + " " + receiveThread.num;
}
System.out.println(message);
for (ReceiveThread receiveThread : receiveList) {
new SendThread(receiveThread.socket, message).start();
}
} else {
try {
String[] data = new String(b).split(" ", 2);// 以第一个空格,将字符串分成两个部分
int clientNum = Integer.parseInt(data[0]);// 转换为数字,即客户端编号数字
// 将消息发送给指定客户端
for (ReceiveThread receiveThread : receiveList) {
if (receiveThread.num == clientNum) {
new SendThread(receiveThread.socket, "用户" + num + ":" + data[1].trim()).start();
}
}
} catch (Exception e) {
new SendThread(socket, "输入错误,请重新输入").start();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {// 关闭资源
if (inputStream != null) {
inputStream.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 发送消息的线程
class SendThread extends Thread {
Socket socket;
String str;
public SendThread(Socket socket, String str) {
this.socket = socket;
this.str = str;
}
@Override
public void run() {
super.run();
try {
socket.getOutputStream().write(str.getBytes());
//new jiemian1 ();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Tools
package chat;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Tools {
DBConn conn;
Connection connection;
public Tools() {
conn = new DBConn();
connection = conn.getConn();
}
public static String dataFormat() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
return format.format(date);
}
// 将byte数组置空
public static byte[] resetArray(byte[] a) {
byte[] b2 = new byte[a.length];
for (int i = 0; i < a.length; i++) {
a[i] = b2[i];
}
return a;
}
// 去除byte数组多余部分
public static byte[] splitByte(byte b[]) {
int i = 0;
for (; i < b.length; i++) {
if (b[i] == 0) {
break;
}
}
byte[] b2 = new byte[i];
for (int j = 0; j < i; j++) {
b2[j] = b[j];
}
return b2;
}
//将数据存入到数据库中
public int insertData(String send, String detail, String content, String receive) throws SQLException {
int result = 0;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
PreparedStatement st = connection
.prepareStatement("insert into chat(send,data,data1,datetime,receive) values(?,?,?,?,?)");
st.setString(1, send);
st.setString(2, detail);
st.setString(3, content);
st.setString(4, format.format(date));
st.setString(5, receive);
result = st.executeUpdate();
return result;
}
//获取所有的用户列表
public ResultSet getData() throws SQLException {
ResultSet result;
connection = conn.getConn();
PreparedStatement st1 = connection.prepareStatement("select client from client");
// PreparedStatement st1 = connection.prepareStatement("select DISTINCT send
// from chat WHERE data='发'");
result = st1.executeQuery();
return result;
}
//查看用户其特定用户之间的信息交流
public ResultSet getData1(String send, String receive) throws SQLException {
ResultSet set;
PreparedStatement st;
st = connection.prepareStatement("select * from chat where send=? and receive=?");
st.setString(1, send);
st.setString(2, receive);
set = st.executeQuery();
return set;
}
// 获取后面的有效数字
public static int getNum(String data) {
int num = 0;
String getresultnum = "";
for (int i = 0; i < data.length(); i++) {
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(data.substring(i, i + 1));
if (isNum.matches()) {
getresultnum += data.substring(i, i + 1);
} else
getresultnum = "";
}
if (!getresultnum.equals("")) {
num = Integer.parseInt(getresultnum);
}
return num;
}
}
DBConn
package chat;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBConn {
private Connection dbConn;
private String dbUrl = "jdbc:mysql://localhost:3306/chatgram";
private String dbUser = "root";
private String dbPwd = "123456";
private String err;
public DBConn() {
err = "";
dbConn = null;
}
public Connection getConn() {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
dbConn = DriverManager.getConnection(dbUrl, dbUser, dbPwd);
}
catch (Exception ex) {
dbConn = null;
err = ex.toString();
System.out.println(err);
}
return dbConn;
}
public String getErr() {
return err;
}
}
chatgram.sql
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 80017
Source Host : localhost:3306
Source Database : chatgram
Target Server Type : MYSQL
Target Server Version : 80017
File Encoding : 65001
Date: 2022-09-04 21:22:02
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for chat
-- ----------------------------
DROP TABLE IF EXISTS `chat`;
CREATE TABLE `chat` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`send` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`data` varchar(10) DEFAULT NULL,
`data1` varchar(100) DEFAULT NULL,
`datetime` varchar(100) DEFAULT NULL,
`receive` varchar(12) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=317 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of chat
-- ----------------------------
INSERT INTO `chat` VALUES ('239', '用户0', '发', 'hello', '2022-07-02 16:44:29', '用户1');
INSERT INTO `chat` VALUES ('240', '用户1', '接', 'hello', '2022-07-02 16:44:29', '用户0');
INSERT INTO `chat` VALUES ('241', '用户0', '发', '你好', '2022-07-02 16:54:05', '用户1');
INSERT INTO `chat` VALUES ('242', '用户1', '接', '你好', '2022-07-02 16:54:05', '用户0');
INSERT INTO `chat` VALUES ('243', '用户1', '发', '我是apple', '2022-07-02 16:55:42', '用户0');
INSERT INTO `chat` VALUES ('244', '用户0', '接', '我是apple', '2022-07-02 16:55:42', '用户1');
INSERT INTO `chat` VALUES ('245', '用户0', '发', '我是boy', '2022-07-02 16:55:52', '用户1');
INSERT INTO `chat` VALUES ('246', '用户1', '接', '我是boy', '2022-07-02 16:55:52', '用户0');
INSERT INTO `chat` VALUES ('307', '用户1', '发', '2022/7/3', '2022-07-03 10:03:20', '用户0');
INSERT INTO `chat` VALUES ('308', '用户0', '接', '2022/7/3', '2022-07-03 10:03:20', '用户1');
INSERT INTO `chat` VALUES ('309', '用户1', '发', '你下线了', '2022-07-03 10:10:11', '用户0');
INSERT INTO `chat` VALUES ('310', '用户0', '接', '你下线了', '2022-07-03 10:10:11', '用户1');
INSERT INTO `chat` VALUES ('311', '用户1', '发', '你好我是用户1', '2022-07-04 13:04:25', '用户0');
INSERT INTO `chat` VALUES ('312', '用户0', '接', '你好我是用户1', '2022-07-04 13:04:25', '用户1');
INSERT INTO `chat` VALUES ('313', '用户0', '发', '在线', '2022-07-04 13:04:47', '用户2');
INSERT INTO `chat` VALUES ('314', '用户2', '接', '在线', '2022-07-04 13:04:47', '用户0');
INSERT INTO `chat` VALUES ('315', '用户0', '发', '2022、9、4', '2022-09-04 21:11:58', '用户1');
INSERT INTO `chat` VALUES ('316', '用户1', '接', '2022、9、4', '2022-09-04 21:11:58', '用户0');
-- ----------------------------
-- Table structure for client
-- ----------------------------
DROP TABLE IF EXISTS `client`;
CREATE TABLE `client` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`client` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of client
-- ----------------------------
INSERT INTO `client` VALUES ('1', '用户0');
INSERT INTO `client` VALUES ('2', '用户1');
INSERT INTO `client` VALUES ('3', '用户2');
INSERT INTO `client` VALUES ('4', '用户3');
INSERT INTO `client` VALUES ('5', '用户4');
INSERT INTO `client` VALUES ('6', '用户5');
缺点是每开一个线程才代表一个用户上线