有了前面的基础下,我们现在需要写一个界面,把客户端的界面用java.awt和java.swing写出来,设置一下基本参数和布局
import javax.swing.*;
import java.awt.*;
public class LoginUI {
public void showUI(){
// 创建一个窗口对象
JFrame jf = new JFrame();
jf.setSize(430, 550); // 设置窗口大小
jf.setTitle("登录界面"); // 设置窗口标题
jf.setLocationRelativeTo(null); // 将窗口居中显示
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 点击关闭按钮时退出程序
// 设置窗口的布局管理器为流式布局
FlowLayout flow = new FlowLayout();
jf.setLayout(flow);
// 加载并添加图片到窗口
ImageIcon image = new ImageIcon("C:\\Users\\eugene\\Desktop\\1723363954729.png");
JLabel jla = new JLabel(image);
jf.add(jla);
// 创建并添加账号标签
JLabel user = new JLabel("账号:");
jf.add(user);
// 创建并设置账号文本框的尺寸,并添加到窗口
JTextField usernameField = new JTextField();
Dimension dm = new Dimension(350, 30); // 设置文本框的尺寸
usernameField.setPreferredSize(dm); // 应用尺寸设置
jf.add(usernameField);
// 创建并添加密码标签
JLabel pas = new JLabel("密码:");
jf.add(pas);
// 创建并设置密码文本框的尺寸,并添加到窗口
JTextField passwordField = new JTextField();
passwordField.setPreferredSize(dm);
jf.add(passwordField);
// 创建并添加登录按钮
JButton loginBtn = new JButton("登录");
jf.add(loginBtn);
// 创建并添加注册按钮
JButton registerBtn = new JButton("注册");
jf.add(registerBtn);
// 设置窗口为可见
jf.setVisible(true);
}
public static void main(String[] args) {
// 创建LoginUI对象并调用showUI方法显示登录界面
LoginUI loginUI = new LoginUI();
loginUI.showUI();
}
}
图片的地址是自己的,可以自由更换
运行后可以得到简单的一个窗口,有聊天框还有按钮
这将做为客户端的界面
然后,我们先解决登录的问题,这需要读取文本框里的内容,并建立一个保存账号密码的数组与之进行比对,然后将结果输出
package com.clclient0809;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class LoginUI {
public static void main(String[] args) {
// 创建MClient对象
MClient mClient = new MClient();
// 创建UserManage对象,并将mClient对象传递进去
UserManage userManage = new UserManage(mClient);
// 创建并显示登录界面
new LoginUI(userManage).showUI();
}
private UserManage userManage;
// 构造方法,接收一个UserManage对象并将其赋值给成员变量userManage
public LoginUI(UserManage userManage){
this.userManage = userManage;
}
public void showUI() {
// 创建窗口对象
JFrame jf = new JFrame();
// 设置窗口的大小(宽430,高550)
jf.setSize(430, 550);
// 设置窗口标题为“登录界面”
jf.setTitle("登录界面");
// 将窗口居中显示
jf.setLocationRelativeTo(null);
// 设置默认关闭操作,退出程序
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置窗口的布局管理器为流式布局
FlowLayout flow = new FlowLayout();
jf.setLayout(flow);
// 加载图片并添加到窗口的标签中
ImageIcon image = new ImageIcon("C:\\Users\\eugene\\Desktop\\1723364705862.png");
JLabel jla = new JLabel(image);
jf.add(jla);
// 创建并添加“账号”标签
JLabel user = new JLabel("账号:");
jf.add(user);
// 创建并设置账号文本框的尺寸,并添加到窗口
JTextField usernameField = new JTextField();
Dimension dm = new Dimension(350, 30); // 设置文本框的尺寸
usernameField.setPreferredSize(dm); // 应用尺寸设置
jf.add(usernameField);
// 创建并添加“密码”标签
JLabel pas = new JLabel("密码:");
jf.add(pas);
// 创建并设置密码文本框的尺寸,并添加到窗口
JTextField passwordField = new JTextField();
passwordField.setPreferredSize(dm);
jf.add(passwordField);
// 创建并添加登录按钮
JButton loginBtn = new JButton("登录");
jf.add(loginBtn);
// 创建并添加注册按钮
JButton registerBtn = new JButton("注册");
jf.add(registerBtn);
// 为登录按钮添加点击事件监听器(匿名内部类实现)
loginBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 按钮被点击时输出信息
System.out.println("按钮启动了");
// 获取输入的账号和密码
String username = usernameField.getText();
String password = passwordField.getText();
// 调用userManage对象的login方法进行验证
if(userManage.login(username,password)){
System.out.println("登录成功...");
}else{
System.out.println("登录失败...");
}
}
});
// 设置窗口为可见,确保所有组件在设置可见之前已被添加到窗口中
jf.setVisible(true);
}
}
同时为了保持一致性,我们把一些静态的值写在一个接口里,这样的接口应该在服务端和客户端都要写一个
package comserver240808;
public interface MsgType {
//登录
public static final String LOGIN = "login";
//注册
public static final String REGISTER = "REGISTER";
public static final String SUCCESS = "SUCCESS";
public static final String ERROR = "ERROR";
}
重写了按键点击后的内容,这里我是新写了一个类,调用里面的方法进行判断
package com.clclient0809;
public class UserManage implements MsgType{
private MClient mClient;
public UserManage( MClient mClient){
this.mClient = mClient;
}
//登录
public boolean login(String username,String password){
//发送账号,密码
mClient.sendMessage(LOGIN+":"+username+":"+password);
System.out.println("发送账号密码");
//读取服务端结果
String response = mClient.readMsg();
System.out.println("读取服务端的结果");
if(SUCCESS.equals(response)){
return true;
}
return false;
}
}
它将文字框的信息处理为LOGIN+":"+username+":"+password的形式发送给服务端并且读取返回的结果,最后以boolean的形式返回
为了保存和检索用户,我们单独建立一个User类
package comserver240808;
public class User {
private String username;
private String password;
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 User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
}
在改进一下服务端,加入一些默认的好友用于测试,同时因为收发消息的功能应该位于登录之后,将其删除,并将这些数据传给线程
package comserver240808;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MServer {
//自定义服务器
public void creatServer() throws Exception{
//创建服务器
ServerSocket server = new ServerSocket(8899);
System.out.println("启动服务器。。。");
//保存所有上线好友
Map<User,Socket> map = new HashMap<>();
//默认注册好友
List<User> userList = new ArrayList<>();
for(int i=1;i<=5;i++){
userList.add(new User(i+"0",i+"0"));
}
while (true) {
//阻塞监听连接过来的客户端
Socket socket = server.accept();
//启动线程跟客户端通信
ServerThread st = new ServerThread(socket,map,userList);
new Thread(st).start();
}
}
public static void main(String[] args) {
try {
new MServer().creatServer();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
最后,将判断账号密码是否正确的方法加在线程里
package comserver240808;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;
import java.util.Map;
// 负责与客户端通信的线程
public class ServerThread implements Runnable, MsgType {
private Socket socket;
private InputStream inputStream;
private OutputStream outputStream;
private Map<User, Socket> map; // 已上线的好友
private Server server;
private List<User> userList; // 已注册的好友列表
// 构造方法,初始化socket、map、userList,并创建Server对象
public ServerThread(Socket socket, Map<User, Socket> map, List<User> userList) {
this.socket = socket;
this.map = map;
this.userList = userList;
server = new Server(socket);
}
// 线程的run方法,持续监听和处理客户端消息
@Override
public void run() {
while (true) {
try {
// 读取客户端发送的消息
String msg = server.readMsg();
System.out.println("msg :" + msg);
// 根据分隔符“:”分割消息,得到消息类型和内容
String[] msgArr = msg.split(":");
switch (msgArr[0]) {
case LOGIN: // 处理登录请求
handleLogin(msgArr[1], msgArr[2]);
System.out.println("msgArr[1],msgArr[2]" + msgArr[1] + msgArr[2]);
break;
case REGISTER: // 处理注册请求
// 注册的处理逻辑可以在这里实现
break;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 处理登录逻辑
private void handleLogin(String username, String password) {
for (User user : userList) {
// 检查用户名和密码是否匹配
if (user.getUsername().equals(username) && user.getPassword().equals(password)) {
// 如果匹配,发送登录成功的消息
server.sendMessage(SUCCESS);
return;
}
}
// 如果所有用户都不匹配,发送登录失败的消息
server.sendMessage(ERROR);
}
// 从客户端读取数据
public String readMsg(InputStream inputStream) throws Exception {
StringBuilder stringBuilder = new StringBuilder();
int b = 0;
// 读取输入流中的数据,直到遇到回车符(13)为止
while ((b = inputStream.read()) != 13) {
// 将读取到的字符拼接成一条完整的消息
stringBuilder.append((char) b);
}
// 返回去掉首尾空白字符的消息字符串
return stringBuilder.toString().trim();
}
}
最后验证一下结果
10 10 是默认的用户,看到状态栏显示登录成功
11 11 不存在这样的用户,识别出来不匹配,状态栏显示登陆失败
至此我们实现了登录的功能。