本实验代码说明:
本实验代码在 vscode1.63.1上运行实证吗,没有问题,且GUI界面完全仿照实验要求报告制作
本代码的Java环境为Java 11.06,并非教学时使用的Java 8 还请注意
本代码运行时请先优先运行server端,设置合适端口,再运行client端,但请注意本代码仍有bug,受限于本人时间与精力没有解决(当前代码只能实现单server对预先代码中设置的client数量连接,且在没有连接足够的客户端时,GUI线程会阻塞),注意,注意。
运行前记得改包名
Server端:
package experiment.experiment3;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class Server {
public static void main(String args[]) {
Myframe frame = new Myframe();
password passwordFrame = new password();
passwordFrame.UI(frame);
frame.UI();
}
}
class password extends Frame {
private static final String PASSWORD = "2021218999"; //登录密码,写死了
private Toolkit kit = Toolkit.getDefaultToolkit(); // 获取工具包
private Dimension screenSize = kit.getScreenSize(); // 获取窗体宽高,并将其赋值给screenSize封装
private Frame MainFrame = new Frame("登录"); // 主窗口
private JPanel passwordJPanel = new JPanel(); // 组装用JPanel
private TitledBorder passwordBorder = new TitledBorder("输入密码:"); // 标题
private static Font defaultFont = new Font("SimSun", Font.PLAIN, 12); // 默认字体
private Box passwordBox = Box.createHorizontalBox(); // 密码输入组件组装box
private JLabel passwordJLabel = new JLabel("密码:"); // 密码输入提示
private Button passwordButton = new Button("确认"); // 密码输入确认按钮
private TextField passworTextField = new TextField(20); // 密码输入框
public void UI(Myframe frame) {
int screenWidth = screenSize.width; // 获取屏幕的宽
int screenHeight = screenSize.height; // 获取屏幕的高
int windowWidth = 400; // 获得窗口宽
int windowHeight = 100;// 获得窗口高
MainFrame.setSize(new Dimension(windowWidth, windowHeight)); // 设置窗口大小
MainFrame.setLocation(screenWidth / 2 - windowWidth / 2, screenHeight / 2 - windowHeight / 2); // 设置窗体位置
passworTextField.setEchoChar('*'); //设置密码回显字符,输入密码显示“*”
// 窗体组装
passwordBox.add(passwordJLabel);
passwordBox.add(passworTextField);
passwordBox.add(passwordButton);
passwordBorder.setTitleFont(defaultFont);
passwordJPanel.setBorder(passwordBorder);
passwordJPanel.add(passwordBox);
MainFrame.add(passwordJPanel);
// 给主窗体添加事件侦听器,使其可以关闭
MainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
// 给按钮的添加事件侦听器,其作用如下:当文本输入框输入的文本和预设的密码一致,则使密码输入窗口消失,同时使用MyFrame类中的setShow方法,使MyFrame的frame对象显示
passwordButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String password;
try {
password = passworTextField.getText();
if (password.equals(PASSWORD)) {
MainFrame.dispose();
frame.setShow();
}
} catch (Exception er) {
er.printStackTrace();
}
}
});
MainFrame.setVisible(true);
}
}
class Myframe extends Frame {
private Toolkit kit = Toolkit.getDefaultToolkit(); // 获取工具包
private Dimension screenSize = kit.getScreenSize(); // 获取窗体宽高,并将其赋值给screenSize封装
// 主窗体
private Frame MainFrame = new Frame("服务端"); // 主窗口
// 端口设置窗体
private JPanel PortPanel = new JPanel();
private TitledBorder titledBorder1 = new TitledBorder("服务器设置"); // 端口设置带边框标题的输入框
private Box PortBox = Box.createHorizontalBox(); // Box容器,组装服务器设置输入框
private Label PortLabel = new Label("端口号:"); // 端口输入框提示文本
private TextField portTextField = new TextField(5); // 端口输入框,同时由于Windows的端口号最大为65535所以设置输入框最大输入列为5
private Button connecButton = new Button("确认"); // 端口输入确认按钮
private static Font defaultFont = new Font("SimSun", Font.PLAIN, 12); // 默认字体
// 消息显示窗体
private JPanel messageJPanel = new JPanel();
private TextArea messageArea = new TextArea(60, 90); // 聊天文本框,显示聊天记录
// 消息输入窗体
private JPanel sayJPanel = new JPanel();
private Box sayBox = Box.createHorizontalBox(); // box容器,组装发言输入框
private TextField sayTextField = new TextField(90); // 发言输入框,输入要发出的内容
private Button sayButton = new Button("发送"); // 发言确认按钮
public static List<Socket> socketList = new ArrayList<Socket>();
// 窗体组装方法
public void UI() {
int screenWidth = screenSize.width; // 获取屏幕的宽
int screenHeight = screenSize.height; // 获取屏幕的高
int windowWidth = 800; // 获得窗口宽
int windowHeight = 600;// 获得窗口高
MainFrame.setSize(new Dimension(windowWidth, windowHeight)); // 设置窗口大小
MainFrame.setLocation(screenWidth / 2 - windowWidth / 2, screenHeight / 2 - windowHeight / 2); // 设置窗体位置
// 服务器端口设置框体组装
PortBox.add(PortLabel);
PortBox.add(portTextField);
PortBox.add(connecButton);
titledBorder1.setTitleFont(defaultFont); // 设置边框字体
PortPanel.setBorder(titledBorder1); // 为JPanel容器添加边框
PortPanel.add(PortBox); // 将端口组件组装盒子放入JPanel
MainFrame.add(PortPanel, BorderLayout.NORTH); // 将Panel添加到主窗体中
// 消息显示窗体组装
messageJPanel.add(messageArea); // 将消息框放入对应的JPanel容器
MainFrame.add(messageJPanel, BorderLayout.CENTER); // 将消息对应的JPanel容器放入主窗体
// 消息发送窗体组装
sayBox.add(sayTextField); // 将消息输入框放入对应的Box容器
sayBox.add(sayButton); // 将消息发送按钮放入对应的box容器
sayJPanel.add(sayBox); // 将box容器放入对应的JPanel中
MainFrame.add(sayJPanel, BorderLayout.SOUTH); // 将JPanel放入主窗体的下方
ServerHandler serverHandler = new ServerHandler(socketList);
// 端口连接按钮添加事件侦听器,当按钮按下时,开始持续监听来自客户端的请求
connecButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
//建立serversokcet
ServerSocket serverSocket = new ServerSocket(Integer.parseInt(portTextField.getText()));
Socket socket = null;
messageArea.setText("Server starting…\n");
int i=2; //最大客户端数量
while(i>0)
{
i--;
socket = serverSocket.accept();
socketList.add(socket);
new Thread(new Task(socket,messageArea)).start();
}
} catch (Exception er) {
er.printStackTrace();
}
}
});
//给发言按钮添加事件侦听器,当按下后,按钮发送消息
sayButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
serverHandler.SendMessage(sayTextField.getText());
messageArea.append("服务器:"+sayTextField.getText());
sayTextField.setText("");
}
});
// 给主窗体添加事件侦听器,使其可以关闭
MainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
// 方法:设置窗口可见
public void setShow() {
MainFrame.setVisible(true);
}
}
//Task类,实现Runnable接口
class Task implements Runnable{
private Socket socket = null; //存储socket
private TextArea messageArea = null; //存储显示模块
//构造方法;
//获取套接字和消息显示框
public Task(Socket socket,TextArea messageArea)
{
this.socket =socket;
this.messageArea = messageArea;
}
@Override
public void run()
{
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String message = bufferedReader.readLine();
//接受同时发送
for(Socket item : Myframe.socketList)
{
PrintWriter printWriter = new PrintWriter(item.getOutputStream());
printWriter.write(message+"\n");
messageArea.append(message+"\n");
printWriter.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//服务器发言部分的处理
class ServerHandler{
private List<Socket>socketList = new ArrayList<Socket>();
public ServerHandler(List<Socket>soketList)
{
this.socketList = soketList;
}
public void SendMessage(String message)
{
for(Socket item : socketList)
try {
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(item.getOutputStream()));
bufferedWriter.write("服务器:"+message+"\n");
bufferedWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Client端:
package experiment.experiment3;
import java.awt.*;
import javax.swing.Box;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
public class Client {
public static void main(String args[])
{
UserName usn = new UserName();
ClientFrame frame = new ClientFrame();
usn.UI(frame);
frame.UI();
}
}
//用户名输入相关类
class UserName extends Frame {
private Toolkit kit = Toolkit.getDefaultToolkit(); // 获取工具包
private Dimension screenSize = kit.getScreenSize(); // 获取窗体宽高,并将其赋值给screenSize封装
private static Font defaultFont = new Font("SimSun", Font.PLAIN, 12); // 默认字体
private Frame MainFrame = new Frame("昵称输入"); //主窗体
private JPanel titlePanel = new JPanel(); //主Jpanel
private TitledBorder titledBorder = new TitledBorder("输入用户名:"); //边框
private Box titleBox = Box.createHorizontalBox(); //组装用Box
private TextField tf = new TextField(15); //昵称输入框,限制输入字符15个
private Button btn = new Button("确认"); //确认按钮
private String UserName; //用户名
public void UI(ClientFrame frame)
{
int screenWidth = screenSize.width; // 获取屏幕的宽
int screenHeight = screenSize.height; // 获取屏幕的高
int windowWidth = 400; // 获得窗口宽
int windowHeight = 100;// 获得窗口高
MainFrame.setSize(new Dimension(windowWidth, windowHeight)); // 设置窗口大小
MainFrame.setLocation(screenWidth / 2 - windowWidth / 2, screenHeight / 2 - windowHeight / 2); // 设置窗体位置
titleBox.add(tf);
titleBox.add(btn);
titledBorder.setTitleFont(defaultFont);
titlePanel.setBorder(titledBorder);
titlePanel.add(titleBox);
MainFrame.add(titlePanel);
//给按钮添加事件侦听器,当按钮按下后,首先将文本框内的文本赋值给UserName,然后关闭用户名输入窗口,同时打开聊天窗口
btn.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e)
{
try {
UserName = tf.getText();
frame.GetUserName(UserName);
MainFrame.dispose(); //用户名输入窗口隐藏
frame.setShow(); //聊天窗口显示
} catch (Exception er) {
er.printStackTrace();
}
}
});
MainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
MainFrame.setVisible(true);
}
public String GetName()
{
return UserName;
}
}
//客户端界面相关类
class ClientFrame extends Frame {
private Toolkit kit = Toolkit.getDefaultToolkit(); // 获取工具包
private Dimension screenSize = kit.getScreenSize(); // 获取窗体宽高,并将其赋值给screenSize封装
private static Font defaultFont = new Font("SimSun", Font.PLAIN, 12); // 默认字体
//客户端设置窗体
private Frame MainFrame = new Frame();
private TitledBorder titledBorder = new TitledBorder("客户端设置:");
private JPanel titleJPanel = new JPanel();
private Box titlebBox = Box.createHorizontalBox();
private Label lb_ip = new Label("服务器IP:");
private TextField tf_Ip = new TextField(15);
private Label lb_port = new Label("端口:");
private TextField tf_port = new TextField(5);
private Button btn_title = new Button("连接");
// 消息显示窗体
private JPanel messageJPanel = new JPanel(); //容器
private TextArea messageArea = new TextArea(60, 90); //聊天文本框,显示聊天记录
// 消息输入窗体
private JPanel sayJPanel = new JPanel();
private Box sayBox = Box.createHorizontalBox(); //box容器,组装发言输入框
private TextField sayTextField = new TextField(90); //发言输入框,输入要发出的内容
private Button sayButton = new Button("发送"); //发言确认按钮
private String UserName; //用户名
public void GetUserName(String UserName) //从上一个窗口获得用户名
{
this.UserName = UserName;
}
public void UI()
{
int screenWidth = screenSize.width; // 获取屏幕的宽
int screenHeight = screenSize.height; // 获取屏幕的高
int windowWidth = 800; // 获得窗口宽
int windowHeight = 600;// 获得窗口高
MainFrame.setSize(new Dimension(windowWidth, windowHeight)); // 设置窗口大小
MainFrame.setLocation(screenWidth / 2 - windowWidth / 2, screenHeight / 2 - windowHeight / 2); // 设置窗体位置
titlebBox.add(lb_ip);
titlebBox.add(tf_Ip);
titlebBox.add(Box.createHorizontalStrut(100));
titlebBox.add(lb_port);
titlebBox.add(tf_port);
titlebBox.add(Box.createHorizontalStrut(100));
titlebBox.add(btn_title);
titledBorder.setTitleFont(defaultFont);
titleJPanel.setBorder(titledBorder);
titleJPanel.add(titlebBox);
MainFrame.add(titleJPanel,BorderLayout.NORTH);
messageJPanel.add(messageArea);
MainFrame.add(messageJPanel,BorderLayout.CENTER);
// 消息发送窗体组装
sayBox.add(sayTextField); // 将消息输入框放入对应的Box容器
sayBox.add(sayButton); // 将消息发送按钮放入对应的box容器
sayJPanel.add(sayBox); // 将box容器放入对应的JPanel中
MainFrame.add(sayJPanel, BorderLayout.SOUTH); // 将JPanel放入主窗体的下方
Handler handler = new Handler();
btn_title.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
try {
messageArea.append("Connect to server…\n");
Socket socket = new Socket(InetAddress.getByName(tf_Ip.getText()),Integer.parseInt(tf_port.getText()));
handler.GetSocket(socket);
handler.GetUserName(UserName);
messageArea.append("Client connected…\n");
handler.SendMessage("加入聊天");
new Thread(new MessageGet(socket, messageArea)).start();
} catch (Exception er) {
er.printStackTrace();
}
}
});
sayButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e)
{
handler.SendMessage(sayTextField.getText());
sayTextField.setText(""); //输入框置空
}
});
MainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
//方法:setShow
//设置窗口可见
public void setShow()
{
MainFrame.setVisible(true);
}
}
//客户端发言部分的处理
class Handler {
private Socket socket;
private String UserName;
public void GetSocket(Socket socket)
{
this.socket = socket;
}
public void GetUserName(String UserName)
{
this.UserName = UserName;
}
//方法:SendMessage
//处理按钮按下后的处理
public void SendMessage(String message)
{
try {
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write(UserName + ":" + message+"\n");
bufferedWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//类:MessageGet
//实现Runable接口,实时监控输入
class MessageGet implements Runnable{
private Socket socket;
private TextArea messageArea;
public MessageGet(Socket socket,TextArea messageArea)
{
this.socket = socket;
this.messageArea = messageArea;
}
@Override
public void run()
{
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true)
{
String message = bufferedReader.readLine();
messageArea.append(message+"\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}