一、实验内容:
(1) 编写一个网络聊天程序,要求服务端和客户端都能够显示聊天过程(类似QQ或微信,能够显示发送者和接收时间)。(基础)
(2) 服务端和客户端都能够显示历史消息,每条消息要有发送者和时间戳。
二、步骤:
1. 编写服务端代码(Server.java)
- 创建Server类,并在main方法中初始化ServerSocket以监听客户端连接。
- 当客户端连接时,获取输入输出流并创建一个新线程用于接收客户端发送的消息。
- 在主线程中获取控制台输入,并将输入发送给客户端。
2. 编写客户端代码(Client.java)
- 创建Client类,并在main方法中创建Socket并连接到服务端。
- 获取输入输出流并创建一个新线程用于接收服务端发送的消息。
- 在主线程中获取控制台输入,并将输入发送给服务端。
3. 显示历史消息
- 在服务端和客户端代码中,使用循环遍历
chatHistory
列表,并打印出历史消息。
三、代码:
服务端:
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Server {
private static int port = 12345;
private static List<String> chatHistory = new ArrayList<>();
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server is running on port " + port);
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("New client connected");
InputStreamReader inputStreamReader = new InputStreamReader(clientSocket.getInputStream());
BufferedReader reader = new BufferedReader(inputStreamReader);
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
// 创建一个新线程用于接收客户端发送的消息
new Thread(() -> {
try {
while (true) {
String message = reader.readLine();
if (message != null) {
String formattedMessage = "[" + getFormattedTime() + "] Client: " + message;
System.out.println(formattedMessage);
chatHistory.add(formattedMessage); // 记录聊天历史
writer.println("Message received: " + message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 在主线程中获取控制台输入,并将输入发送给客户端
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
String consoleInput;
while ((consoleInput = consoleReader.readLine()) != null) {
writer.println(consoleInput);
String formattedMessage = "[" + getFormattedTime() + "] Server: " + consoleInput;
System.out.println(formattedMessage);
chatHistory.add(formattedMessage); // 记录聊天历史
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static String getFormattedTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
}
客户端:
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Client {
private static String serverAddress = "127.0.0.1";
private static int serverPort = 12345;
public static void main(String[] args) {
try {
Socket socket = new Socket(serverAddress, serverPort);
System.out.println("Connected to server");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
// 创建一个新线程用于接收服务端发送的消息
new Thread(() -> {
try {
while (true) {
String message = reader.readLine();
if (message != null) {
System.out.println(message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 在主线程中获取控制台输入,并将输入发送给服务端
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
String consoleInput;
while ((consoleInput = consoleReader.readLine()) != null) {
writer.println(consoleInput);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、程序测试:
1. 分别运行Server和Client程序。
2. 在Client程序中输入消息,可以在Server程序的控制台中看到接收到的消息,并且在Client程序的控制台也能看到消息。
3. 在Server程序中输入消息,可以在Client程序的控制台中看到接收到的消息,并且在Server程序的控制台也能看到消息。
4. 结束程序运行时,在Server和Client程序的控制台中都会显示完整的聊天历史记录,包括发送者和时间戳信息。
以上程序实现了一个简单的网络聊天程序,能够显示聊天过程和历史消息,可以作为基础版本进行进一步扩展和优化。
五、遇到的问题和解决办法
- 问题:多个客户端同时连接的情况下,如何确保每个客户端都能正常收发消息?
- 解决办法:为每个客户端连接创建一个新线程,用于不断接收消息。这样可以确保每个客户端都可以实时收取并发送消息。
- 问题:是否需要处理多线程下的并发操作?
- 解决办法:在本实验中,未对多个线程同时访问
chatHistory
进行并发控制。在实际开发中,要考虑使用线程安全的集合或加锁来确保多个线程对chatHistory
的操作不会出现并发问题。
- 问题:如何在控制台中优雅地显示聊天记录和历史消息?
- 解决办法:在控制台中使用特定的格式显示聊天记录和历史消息,以便于用户阅读。
六、实验心得
-
多线程编程的重要性: 在网络聊天程序中,多线程的使用非常重要,因为需要同时进行接收和发送消息的操作。了解多线程编程可以帮助更好地处理并发消息操作。
-
网络编程的挑战: 网络编程中需要处理各种不可预测的情况,如连接中断、消息丢失等。编写网络应用程序需要更多的健壮性和容错性。
-
历史消息的管理: 在聊天程序中,管理历史消息是一项重要任务。对于大型应用程序,可以考虑使用数据库来管理历史消息,以便于进行持久化和查询。
-
用户界面的重要性: 在实际应用中,控制台界面并不适用于网络聊天程序。考虑使用图形用户界面(GUI)或移动应用来改善用户体验。