随着计算机的普及,网络编程也显得愈发重要,同时掌握实现客户/服务器程序的编写方法也不可忽视。通过学习,我们将了解TCP协议在网络通信中的重要性,掌握如何使用在Linux或Windows平台上编写简单的TCP客户/服务器程序。课程设计分为两个部分:服务器端和客户端。在服务器端,我们将学习如何创建TCP/IP套接字、绑定套接字到端口、监听传入的连接以及处理连接请求。在客户端,我们将学习如何创建TCP/IP套接字、连接到服务器端口、发送数据、接收响应数据以及关闭连接。通过完成这些任务,我们将能够理解网络编程的基本原理,包括套接字的创建和使用、数据的发送和接收、以及连接的建立和关闭。这门课程设计旨在帮助我们建立起对网络编程的基本认识,为日后深入学习网络编程打下坚实的基础。
1架构设计
客户端-服务器架构:采用客户端-服务器模式,客户端负责发送和接收消息,服务器负责转发消息和管理用户。
单服务器多客户端模式:服务器支持多个客户端连接,实现多用户同时聊天的功能。
用户文件:存储用户信息,如用户名、密码等。
2通信协议
使用TCP协议实现客户端与服务器之间的通信,保证消息的可靠传输。
3客户端设计
用户界面:提供用户友好的界面,包括消息显示区域、消息输入框、在线用户列表等。
消息发送:用户输入消息后,通过TCP协议将消息发送到服务器。
消息接收:接收服务器转发的其他用户消息,并在界面上显示。
4服务器设计
用户管理:维护用户信息和在线状态,处理用户的注册、登录和退出操作。
消息转发:接收客户端消息并转发给指定用户,实现实时聊天功能。
在线用户列表:维护在线用户列表,及时更新并向客户端推送。
5项目结构
6代码
package server;
import client.ClientHandler;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 多线程服务器类,管理客户端连接和用户信息
*/
public class MultiThreadedServer {
// 服务器端口
private static final int PORT = 10001;
// 客户端处理器列表
public static final List<ClientHandler> clients = new ArrayList<>();
// 用户信息
private static Properties userInfo;
// 创建固定大小的线程池
private static final ExecutorService executor = Executors.newFixedThreadPool(10);
/**
* 主方法,启动服务器并监听客户端连接
*/
public static void main(String[] args) {
userInfo = loadUserInfo(); // 加载用户信息
// 创建服务器套接字
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("服务器已启动,等待客户端连接...");
while (true) {
// 接受客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接成功: " + clientSocket);
// 创建客户端处理器
ClientHandler clientHandler = new ClientHandler(clientSocket);
// 添加客户端处理器到列表
clients.add(clientHandler);
// 在线程池中执行客户端处理器
executor.execute(clientHandler);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 加载用户信息
* @return 用户信息
*/
private static Properties loadUserInfo() {
// 创建属性对象
Properties prop = new Properties();
try (FileInputStream fis = new FileInputStream("userinfo.properties")) {
// 加载属性文件
prop.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
// 返回属性对象
return prop;
}
/**
* 注册用户
* @param username 用户名
* @param password 密码
* @return 注册结果
*/
public static synchronized boolean registerUser(String username, String password) {
// 如果用户信息中不包含用户名
if (!userInfo.containsKey(username)) {
// 设置用户名和密码
userInfo.setProperty(username, password);
//保存用户信息
saveUserInfo();
return true;
}
// 返回注册失败
return false;
}
/**
* 验证用户身份
* @param username 用户名
* @param password 密码
* @return 验证结果
*/
public static synchronized boolean authenticateUser(String username, String password) {
// 获取存储的密码
String storedPassword = userInfo.getProperty(username);
// 返回密码是否匹配
return storedPassword != null && storedPassword.equals(password);
}
/**
* 保存用户信息
*/
private static synchronized void saveUserInfo() {
try (FileOutputStream fos = new FileOutputStream("userinfo.properties")) {
// 存储用户信息到文件
userInfo.store(fos, null);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 广播消息给所有客户端
* @param message 消息内容
*/
public static void broadcastMessage(String message) {
// 遍历客户端处理器列表
for (ClientHandler client : clients) {
// 发送消息给客户端
client.sendMessage(message);
}
}
/**
* 移除客户端
* @param clientHandler 客户端处理器
*/
public static void removeClient(ClientHandler clientHandler) {
// 从客户端处理器列表中移除客户端处理器
clients.remove(clientHandler);
// 广播用户下线消息
broadcastMessage("用户 "
+ clientHandler.getUsername() +
" 下线了");
// 广播在线用户列表
broadcastOnlineUsers();
}
/**
* 广播在线用户列表
*/
public static void broadcastOnlineUsers() {
// 创建在线用户列表字符串
StringBuilder onlineUsers = new StringBuilder("在线用户列表:\n");
// 遍历客户端处理器列表
for (ClientHandler client : clients) {
// 添加用户名到在线用户列表字符串
onlineUsers.append(client.getUsername()).append("\n");
}
// 广播在线用户列表消息
broadcastMessage(onlineUsers.toString());
}
}
package client;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.Socket;
/**
* 客户端 GUI 类,实现用户登录、注册和聊天功能
*/
public class ClientGUI {
// 服务器主机
private static final String SERVER_HOST = "127.0.0.1";
// 服务器端口
private static final int SERVER_PORT = 10001;
// 客户端套接字
private static Socket socket;
// 读取服务器消息的输入流
private static BufferedReader in;
// 发送消息到服务器的输出流
private static BufferedWriter out;
// 聊天消息显示区域
private static JTextArea chatArea;
// 消息输入框
private static JTextField messageField;
// 用户名输入框
private static JTextField usernameField;
// 密码输入框
private static JPasswordField passwordField;
/**
* 主方法,创建登录界面并连接服务器
*/
public static void main(String[] args) {
// 创建并显示登录界面
createAndShowLoginGUI();
try {
// 连接服务器
socket = new Socket(SERVER_HOST, SERVER_PORT);
System.out.println("连接成功");
//初始化输入输出流
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 启动服务器消息监听线程
new Thread(new ServerListener()).start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建并显示登录界面
*/
private static void createAndShowLoginGUI() {
JFrame frame = new JFrame("登录"); // 创建登录界面窗口
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作
frame.setSize(300, 200); // 设置窗口大小
frame.setLayout(new BorderLayout()); // 设置布局
JPanel loginPanel = new JPanel(new GridLayout(3, 2)); // 创建登录面板
loginPanel.add(new JLabel("用户名:")); // 添加用户名标签
usernameField = new JTextField(); // 创建用户名输入框
loginPanel.add(usernameField); // 添加用户名输入框
loginPanel.add(new JLabel("密码:")); // 添加密码标签
passwordField = new JPasswordField(); // 创建密码输入框
loginPanel.add(passwordField); // 添加密码输入框
JButton loginButton = new JButton("登录"); // 创建登录按钮
loginButton.addActionListener(e -> { // 添加登录按钮点击事件
String username = usernameField.getText().trim(); // 获取用户名
String password = new String(passwordField.getPassword()).trim(); // 获取密码
sendLoginMessage(username, password); // 发送登录消息
frame.dispose(); // 关闭登录界面
});
loginPanel.add(loginButton); // 添加登录按钮
JButton registerButton = new JButton("注册"); // 创建注册按钮
registerButton.addActionListener(e -> { // 添加注册按钮点击事件
String username = usernameField.getText().trim(); // 获取用户名
String password = new String(passwordField.getPassword()).trim(); // 获取密码
sendRegisterMessage(username, password); // 发送注册消息
});
loginPanel.add(registerButton); // 添加注册按钮
frame.add(loginPanel, BorderLayout.CENTER); // 将登录面板添加到窗口中
frame.setVisible(true); // 显示窗口
frame.setLocationRelativeTo(null); // 居中显示
}
/**
* 创建并显示聊天界面
*/
private static void createAndShowChatGUI() {
JFrame frame = new JFrame("聊天室"); // 创建聊天界面窗口
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作
frame.setSize(400, 300); // 设置窗口大小
frame.setLayout(new BorderLayout()); // 设置布局
chatArea = new JTextArea(); // 创建聊天消息显示区域
chatArea.setEditable(false); // 设置为不可编辑
JScrollPane scrollPane = new JScrollPane(chatArea); // 创建滚动面板
frame.add(scrollPane, BorderLayout.CENTER); // 将滚动面板添加到窗口中
JPanel inputPanel = new JPanel(new BorderLayout()); // 创建输入面板
messageField = new JTextField(); // 创建消息输入框
JButton sendButton = new JButton("发送"); // 创建发送按钮
sendButton.addActionListener(e -> { // 添加发送按钮点击事件
String message = messageField.getText().trim(); // 获取消息内容
sendMessage(message); // 发送消息
messageField.setText(""); // 清空消息输入框
});
inputPanel.add(messageField, BorderLayout.CENTER); // 将消息输入框添加到输入面板
inputPanel.add(sendButton, BorderLayout.EAST); // 将发送按钮添加到输入面板
frame.add(inputPanel, BorderLayout.SOUTH); // 将输入面板添加到窗口中
frame.setVisible(true); // 显示窗口
frame.setLocationRelativeTo(null); // 居中显示
}
/**
* 发送登录消息
* @param username 用户名
* @param password 密码
*/
private static void sendLoginMessage(String username, String password) {
try {
// 发送登录消息格式为 "login:用户名:密码"
out.write("login:" + username + ":" + password + "\n");
// 刷新输出流
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送注册消息
* @param username 用户名
* @param password 密码
*/
private static void sendRegisterMessage(String username, String password) {
try {
// 发送注册消息格式为 "register:用户名:密码"
out.write("register:" + username + ":" + password + "\n");
// 刷新输出流
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送消息
* @param message 消息内容
*/
private static void sendMessage(String message) {
try {
// 发送消息内容
out.write(message + "\n");
// 刷新输出流
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务器消息监听线程
*/
private static class ServerListener implements Runnable {
@Override
public void run() {
try {
String message;
// 循环读取服务器消息
while ((message = in.readLine()) != null) {
switch (message) {
case "login_success" -> {
System.out.println("登录成功");
// 创建并显示聊天界面
createAndShowChatGUI();
}
case "login_fail" -> {
System.out.println("登录失败");
// 弹出登录失败对话框
JOptionPane.showMessageDialog(null,
"登录失败,请检查用户名和密码",
"登录失败",
JOptionPane.ERROR_MESSAGE);
}
case "register_success" -> {
System.out.println("注册成功");
// 弹出注册成功对话框
JOptionPane.showMessageDialog(null,
"注册成功,请登录",
"注册成功",
JOptionPane.INFORMATION_MESSAGE);
}
case "register_fail" -> {
System.out.println("注册失败");
// 弹出注册失败对话框
JOptionPane.showMessageDialog(null,
"注册失败,用户名已存在",
"注册失败",
JOptionPane.ERROR_MESSAGE);
}
// 添加其他消息到聊天消息显示区域
default -> chatArea.append(message + "\n");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package client;
import server.MultiThreadedServer;
import java.io.*;
import java.net.Socket;
/**
* 客户端处理器类,实现了Runnable接口,用于处理客户端的消息和连接
*/
public class ClientHandler implements Runnable {
// 客户端套接字
private final Socket socket;
// 读取客户端消息的输入流
private BufferedReader in;
// 向客户端发送消息的输出流
private BufferedWriter out;
// 客户端用户名
private String username;
/**
* 构造函数,初始化客户端处理器
*
* @param socket 客户端套接字
*/
public ClientHandler(Socket socket) {
this.socket = socket;
try {
// 初始化输入流和输出流
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 线程运行函数,处理客户端消息
*/
@Override
public void run() {
try {
String message;
// 循环读取客户端消息
while ((message = in.readLine()) != null) {
if (message.startsWith("login:")) {
// 处理登录请求
String[] parts = message.split(":");
String username = parts[1];
String password = parts[2];
handleLogin(username, password);
} else if (message.startsWith("register:")) {
// 处理注册请求
String[] parts = message.split(":");
String username = parts[1];
String password = parts[2];
handleRegister(username, password);
} else if (message.startsWith("private:")) {
// 处理私聊消息
handlePrivateMessage(message);
} else {
// 发送消息到所有客户端
MultiThreadedServer.broadcastMessage(username + ": " + message);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭套接字和从客户端的输入输出流
socket.close();
MultiThreadedServer.removeClient(this);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 处理登录请求
*
* @param username 用户名
* @param password 密码
*/
private void handleLogin(String username, String password) {
if (MultiThreadedServer.authenticateUser(username, password)) {
// 登录成功,发送消息给客户端
sendMessage("login_success");
this.username = username;
// 广播用户上线消息给所有客户端
MultiThreadedServer.broadcastMessage("用户 " + username + " 上线了");
// 广播在线用户列表给所有客户端
MultiThreadedServer.broadcastOnlineUsers();
} else {
// 登录失败,发送消息给客户端
sendMessage("login_fail");
}
}
/**
* 处理注册请求
*
* @param username 用户名
* @param password 密码
*/
private void handleRegister(String username, String password) {
if (MultiThreadedServer.registerUser(username, password)) {
// 注册成功,发送消息给客户端
sendMessage("register_success");
} else {
// 注册失败,发送消息给客户端
sendMessage("register_fail");
}
}
/**
* 处理私聊消息
*
* @param message 私聊消息内容
*/
private void handlePrivateMessage(String message) {
// 分割私聊消息
String[] parts = message.split(":", 3);
String targetUsername = parts[1];
String privateMessage = parts[2];
// 查找目标客户端
ClientHandler targetClient = findClientByUsername(targetUsername);
if (targetClient != null) {
// 如果目标客户端在线,发送私聊消息给目标客户端,并发送确认消息给发送方
targetClient.sendMessage("(私聊) " + username + ": " + privateMessage);
sendMessage("私聊消息已发送给 " + targetUsername + ": " + privateMessage);
} else {
// 如果目标客户端不在线,发送消息给发送方
sendMessage("用户 " + targetUsername + " 不存在或不在线");
}
}
/**
* 根据用户名查找客户端,并返回相应的ClientHandler对象。
*
* @param username 要查找的用户名
* @return 如果找到匹配的客户端,则返回对应的ClientHandler对象;否则返回null。
*/
private ClientHandler findClientByUsername(String username) {
// 遍历客户端列表,查找匹配的用户名
for (ClientHandler client : MultiThreadedServer.clients) {
// 如果客户端用户名不为null且与目标用户名相匹配,则返回该客户端
if (client.getUsername() != null && client.getUsername().equals(username)) {
return client;
}
}
// 如果未找到匹配的客户端,则返回null
return null;
}
/**
* 向客户端发送消息。
*
* @param message 要发送的消息内容
*/
public void sendMessage(String message) {
try {
// 将消息写入输出流并刷新
out.write(message + "\n");
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取客户端的用户名。
* @return 客户端的用户名
*/
public String getUsername() {
return username;
}
}
7运行结果
首先要先启动服务端,再启动客户端
用户信息如下:
测试注册功能:
回去查看结果
测试登录功能:
测试多用户登录系统:
测试群聊信息:
测试私聊信息:
测试用户下线功能:
8总结
本聊天室系统是基于客户端-服务器架构设计的,通过TCP协议实现客户端与服务器之间的通信。系统具备了用户注册、登录、实时聊天、用户列表和聊天记录等基本功能,同时保证了系统的安全、可靠和性能良好。数据库设计合理,用户信息和聊天记录得以有效存储和管理。客户端提供了直观友好的界面,使用户操作简便。服务器实现了用户管理、消息转发和在线用户列表维护等核心功能。整体而言,该聊天室系统具备了一定的实用性和可靠性,能够满足用户的基本聊天需求。
标签:username,聊天室,String,课设,TCP,static,private,new,客户端 From: https://blog.csdn.net/qq_61062215/article/details/140149967