基于JAVA的聊天室

1.ChatRoom项目介绍

Create a chatroom with Java

项目配置:IntelliJ(jdk11),MySQL5

本文件会不断更新功能

GitHub账号:Guotianyu-2020

2022.2.13 Version1

此版本实现的功能有:本机上的两个客户端可以随意接收发送信息,并将两者发送的信息保存在MySQL数据库当中

此版本使用到的知识有:简单的java基础,多线程,JDBC连接,MySQL,Java服务器编程,极少的计算机网络知识

未来版本展望:制作UI界面,可以任意添加客户端数量

项目结构:

src目录下的三个目录分别归属客户端,MySQL数据库和服务器。

Server目录中TCPServer包含了主方法,ServerThread封装了服务器功能。在ServerThread中让服务器的两个线程同时监听两个客户端,将监听内容存入数据库相应表中,再将内容输出给另一个客户端以完成通信。

Client目录包含了两个客户端目录,以及客户端的读线程和写线程。先打开服务器端后,依次打开两个客户端。

MySQL目录下InsertMessage封装了插入方法,将服务器监听到的内容存入数据库表中

2022.3.6 Version2

此版本实现的功能有:在Version1的技术上添加了可视化界面,为每个客户设置单独的输入框和输出框,如图

GUI

此版本使用到的知识有:swing界面设计,多线程

未来版本展望:优化界面设计,设置群聊天,设置登录和注册功能

项目结构:

Client目录下两个客户端文件各自新增了界面线程,新增ClientGUI.java界面文件

2.源文件

2.1Version1源文件

客户端1、2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package Client;

import java.io.IOException;
import java.net.Socket;

public class TCPClient1 {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("10.238.16.14", 8888); // 服务端IP地址
TCPClient1.runClient1(socket);
}


public static void runClient1(Socket socket) {
ClientWriteThread cwt = new ClientWriteThread(socket);
ClientListenThread clt = new ClientListenThread(socket);
new Thread(() -> {
while (!socket.isClosed()) {
clt.listen();
}
}).start();
new Thread(() -> {
while (!socket.isClosed()) {
cwt.write();
}
}).start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package Client;

import java.io.IOException;
import java.net.Socket;

public class TCPClient2 {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("10.238.16.14", 8888);
TCPClient2.runClient2(socket);
}


public static void runClient2(Socket socket) {
ClientWriteThread cwt = new ClientWriteThread(socket);
ClientListenThread clt = new ClientListenThread(socket);
new Thread(() -> {
while (!socket.isClosed()) {
clt.listen();
}
}).start();
new Thread(() -> {
while (!socket.isClosed()) {
cwt.write();
}
}).start();
}
}

客户端监听进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package Client;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class ClientListenThread {
private Socket socket;

public ClientListenThread(Socket socket) {
this.socket = socket;
}

public void listen() {
InputStream is;
try {
is = this.socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes, 0, len));
} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端写进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package Client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientWriteThread {
private Socket socket;

public ClientWriteThread(Socket socket) {
this.socket = socket;
}

public void write() {
try {
OutputStream os = this.socket.getOutputStream();
Scanner sc = new Scanner(System.in);
String s = sc.next();
os.write(s.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}

服务端主方法:

1
2
3
4
5
6
7
8
9
10
package Server;

import java.io.IOException;

public class TCPServer {
public static void main(String[] args) throws IOException {
ServerThread st = new ServerThread(8888);
st.runServer();
}
}

服务端进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package Server;

import MySQL.InsertMessage;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerThread{
private int port;
private Socket socket1;
private Socket socket2;


public ServerThread(int port) {
this.port = port;
}


public void runServer() throws IOException {
ServerSocket server;
server = new ServerSocket(this.port);
this.socket1 = server.accept();
this.socket2 = server.accept();
System.out.println("两个客户端已经准备好");
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
InputStream is1 = socket1.getInputStream();
byte[] bytes = new byte[1024];
int len1 = is1.read(bytes);
String s1 = new String(bytes, 0, len1);
System.out.println("客户端1的消息是" + s1);
InsertMessage.insert(s1);
if (len1 != -1) {
OutputStream os = socket2.getOutputStream();
os.write(s1.getBytes());
}
System.out.println("已发出客户端1的消息");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();

new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
InputStream is2 = socket2.getInputStream();
byte[] bytes = new byte[1024];
int len2 = is2.read(bytes);
String s2 = new String(bytes, 0, len2);
System.out.println("客户端2的消息是" + s2);
InsertMessage.insert(s2);
if (len2 != -1) {
OutputStream os = socket1.getOutputStream();
os.write(s2.getBytes());
}
System.out.println("已发出客户端2的消息");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}

服务端写入MySQL的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package MySQL;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class InsertMessage {
public static void insert(String massage) {
Statement stmt = null;
Connection conn = null;
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.定义sql
String sql = "insert into message values(null, '" + massage + "');";
// 3.获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql:///chatroom", "root", "200818");
// 4.获取执行sql的对象
stmt = conn.createStatement();
// 5.执行sql
int count = stmt.executeUpdate(sql);
// 6.处理结果
System.out.println("已写入数据库");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
// 7.释放资源,注意要避免空指针异常
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

2.2Version2源文件

客户端1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package Client;

import java.awt.*;
import java.io.IOException;
import java.net.Socket;

public class TCPClient1 {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888); // 服务端IP地址
TCPClient1.runClient1(socket);
}


public static void runClient1(Socket socket) {
ClientWriteThread cwt = new ClientWriteThread(socket);
ClientListenThread clt = new ClientListenThread(socket);
// 听线程
new Thread(() -> {
while (!socket.isClosed()) {
clt.listen();
}
}).start();


// 写线程
new Thread(() -> {
while (!socket.isClosed()) {
if (ClientGUI.words != null) {
cwt.write();
}
}
}).start();


// GUI界面
EventQueue.invokeLater(()->{
ClientGUI.runGUI();
ClientGUI.frame.setTitle("Client1");
});
}
}

写线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package Client;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class ClientWriteThread {
private Socket socket;

public ClientWriteThread(Socket socket) {
this.socket = socket;
}

public void write() {
try {
if (ClientGUI.words != null) {
OutputStream os = this.socket.getOutputStream();
os.write(ClientGUI.words.getBytes());
ClientGUI.outputs.append("\n");
ClientGUI.outputs.append(ClientGUI.words);
ClientGUI.inputs.setText("");
ClientGUI.words = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

读线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package Client;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class ClientListenThread {
private Socket socket;

public ClientListenThread(Socket socket) {
this.socket = socket;
}

public void listen() {
InputStream is;
try {
is = this.socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String s = new String(bytes, 0, len);
if (s != null) {
ClientGUI.outputs.append("\n");
ClientGUI.outputs.append(s);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

GUI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package Client;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ClientGUI {
static JFrame frame = new JFrame();
static String words;
static JButton button = new JButton("发送");
static JTextArea outputs = new JTextArea("消息:");
static JTextArea inputs = new JTextArea();


public static void runGUI(){
// 流式布局
frame.setLayout(new FlowLayout());
// 获取Frame容器
Container container = frame.getContentPane();
// 设置窗体位置,大小
frame.setBounds(400, 20, 400, 300);
// 设置label大小
outputs.setPreferredSize(new Dimension (360,100));
// 设置文本区大小
inputs.setPreferredSize(new Dimension (360,100));
// 设置输入区自动换行
inputs.setLineWrap(true);
// 关闭窗口直接关掉进程
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 输出去设置不可编辑
outputs.setEditable(false);


// 添加组件
container.add(outputs);
container.add(inputs);
container.add(button);
frame.setVisible(true);
// 事件触发不需要单独设置一个线程,放到GUI线程就行
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ClientGUI.words = ClientGUI.inputs.getText();
}
});
}
}
查看评论