1定义静态变量
public class Constant {
public static final String SERVER_IP = "127.0.0.1";
public static final int SERVER_PORT = 6666;
}
2创建登录UI
import javax.swing.*;
import java.awt.*;
import java.io.DataOutputStream;
import java.net.Socket;
public class ChatEntryFrame extends JFrame {
private JTextField nicknameField;
private JButton enterButton;
private JButton cancelButton;
private Socket socket; // 记住当前客户端系统的通信管道
public ChatEntryFrame() {
setTitle("局域网聊天室");
setSize(350, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false); // 禁止调整大小
// 设置背景颜色
getContentPane().setBackground(Color.decode("#F0F0F0"));
// 创建主面板并设置布局
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBackground(Color.decode("#F0F0F0"));
add(mainPanel);
// 创建顶部面板
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
topPanel.setBackground(Color.decode("#F0F0F0"));
// 标签和文本框
JLabel nicknameLabel = new JLabel("昵称:");
nicknameLabel.setFont(new Font("楷体", Font.BOLD, 16));
nicknameField = new JTextField(10);
nicknameField.setFont(new Font("楷体", Font.PLAIN, 16));
nicknameField.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(1, 1, 1, 1, Color.GRAY),
BorderFactory.createEmptyBorder(5, 5, 5, 5)
));
topPanel.add(nicknameLabel);
topPanel.add(nicknameField);
mainPanel.add(topPanel, BorderLayout.NORTH);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
buttonPanel.setBackground(Color.decode("#F0F0F0"));
enterButton = new JButton("登录");
enterButton.setFont(new Font("楷体", Font.BOLD, 16));
enterButton.setBackground(Color.decode("#007BFF"));
enterButton.setForeground(Color.WHITE);
enterButton.setBorderPainted(false);
enterButton.setFocusPainted(false);
cancelButton = new JButton("取消");
cancelButton.setFont(new Font("楷体", Font.BOLD, 16));
cancelButton.setBackground(Color.decode("#DC3545"));
cancelButton.setForeground(Color.WHITE);
cancelButton.setBorderPainted(false);
cancelButton.setFocusPainted(false);
buttonPanel.add(enterButton);
buttonPanel.add(cancelButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
// 添加监听器
enterButton.addActionListener(e -> {
String nickname = nicknameField.getText(); // 获取昵称
nicknameField.setText("");
if (!nickname.isEmpty()) {
try {
login(nickname);
// 进入聊天室逻辑: 启动聊天界面。把昵称传给聊天界面。
new ClientChatFrame(nickname, socket);
this.dispose(); // 关闭登录窗口
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
JOptionPane.showMessageDialog(this, "请输入昵称!");
}
});
cancelButton.addActionListener(e -> System.exit(0));
this.setVisible(true); // 显示窗口
}
public void login(String nickname) throws Exception {
// 立即发送登录消息给服务端程序。
// 1、创建Socket管道请求与服务端的socket链接
socket = new Socket(Constant.SERVER_IP, Constant.SERVER_PORT );
// 2、立即发送消息类型1 和自己的昵称给服务端
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(1); // 消息类型 登录
dos.writeUTF(nickname);
dos.flush();
}
public static void main(String[] args) {
new ChatEntryFrame();
}
}
3创建聊天UI
import javax.swing.*;
import java.awt.*;
import java.io.DataOutputStream;
import java.net.Socket;
public class ClientChatFrame extends JFrame {
public JTextArea smsContent = new JTextArea(23, 50);
private JTextArea smsSend = new JTextArea(4, 40);
public JList<String> onLineUsers = new JList<>();
private JButton sendBn = new JButton("发送");
private Socket socket;
public ClientChatFrame() {
initView();
this.setVisible(true);
}
public ClientChatFrame(String nickname, Socket socket) {
this(); // 先调用上面的无参数构造器,初始化界面信息
// 初始化数据。
// 立马展示昵称到窗口
this.setTitle(nickname + "的聊天窗口");
this.socket = socket;
// 立即把客户端的这个Socket管道交给一个独立的线程专门负责读取客户端socket从服务端收到的在线人数更新数据或者群聊数据。
new ClientReaderThread(socket, this).start();
}
private void initView() {
this.setSize(800, 650);
this.setLayout(new BorderLayout(10, 10));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口,退出程序
this.setLocationRelativeTo(null); // 窗口居中
this.getContentPane().setBackground(Color.decode("#F8F9FA"));
// 统一字体样式
Font mainFont = new Font("微软雅黑", Font.PLAIN, 15);
Font listTitleFont = new Font("微软雅黑", Font.BOLD, 16);
// 消息内容框
smsContent.setFont(mainFont);
smsContent.setBackground(Color.WHITE);
smsContent.setEditable(false);
smsContent.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
smsContent.setLineWrap(true);
smsContent.setWrapStyleWord(true);
// 发送消息框
smsSend.setFont(mainFont);
smsSend.setBackground(Color.WHITE);
smsSend.setWrapStyleWord(true);
smsSend.setLineWrap(true);
smsSend.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(1, 1, 1, 1, Color.decode("#CED4DA")),
BorderFactory.createEmptyBorder(8, 10, 8, 10)
));
// 在线用户列表区域
JPanel userListPanel = new JPanel(new BorderLayout(0, 8));
userListPanel.setBackground(Color.decode("#F8F9FA"));
userListPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JLabel userListTitle = new JLabel("在线用户");
userListTitle.setFont(listTitleFont);
userListTitle.setForeground(Color.decode("#343A40"));
userListTitle.setHorizontalAlignment(SwingConstants.CENTER);
onLineUsers.setFont(mainFont);
onLineUsers.setBackground(Color.WHITE);
onLineUsers.setFixedCellWidth(150);
onLineUsers.setVisibleRowCount(15);
onLineUsers.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.decode("#CED4DA")));
JScrollPane userListScrollPane = new JScrollPane(onLineUsers);
userListScrollPane.setBorder(BorderFactory.createEmptyBorder());
userListScrollPane.setPreferredSize(new Dimension(160, 0));
userListPanel.add(userListTitle, BorderLayout.NORTH);
userListPanel.add(userListScrollPane, BorderLayout.CENTER);
// 底部发送区域
JPanel bottomPanel = new JPanel(new BorderLayout(10, 10));
bottomPanel.setBackground(Color.decode("#F8F9FA"));
bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
JScrollPane smsSendScrollPane = new JScrollPane(smsSend);
smsSendScrollPane.setBorder(BorderFactory.createEmptyBorder());
smsSendScrollPane.setPreferredSize(new Dimension(0, 100));
// 发送按钮
sendBn.setFont(mainFont);
sendBn.setBackground(Color.decode("#409EFF"));
sendBn.setForeground(Color.WHITE);
sendBn.setBorderPainted(false);
sendBn.setFocusPainted(false);
sendBn.setPreferredSize(new Dimension(100, 40));
sendBn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
btnPanel.setBackground(Color.decode("#F8F9FA"));
btnPanel.add(sendBn);
bottomPanel.add(smsSendScrollPane, BorderLayout.CENTER);
bottomPanel.add(btnPanel, BorderLayout.EAST);
// 中心消息区域
JScrollPane smsContentScrollPane = new JScrollPane(smsContent);
smsContentScrollPane.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.decode("#CED4DA")));
smsContentScrollPane.setPreferredSize(new Dimension(0, 0));
// 给发送按钮绑定点击事件
sendBn.addActionListener(e -> {
// 获取输入框中的内容
String msg = smsSend.getText();
// 清空输入框
smsSend.setText("");
// 发送消息
sendMsgToServer(msg);
});
// 组装整体布局
this.add(smsContentScrollPane, BorderLayout.CENTER);
this.add(bottomPanel, BorderLayout.SOUTH);
this.add(userListPanel, BorderLayout.EAST);
}
// 发送消息
private void sendMsgToServer(String msg) {
try {
// 1、从socket管道中得到一个特殊数据输出流
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
// 2、把消息发送给服务端
dos.writeInt(2);
dos.writeUTF(msg);
dos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new ClientChatFrame();
}
// 更新在线用户列表
public void updateOnlineUsers(String[] onLineNames) {
// 把这个线程读取到的在线用户名称展示到界面上
onLineUsers.setListData(onLineNames);
}
// 更新群聊消息
public void setMsgToWin(String msg) {
// 更新群聊消息到界面展示
smsContent.append(msg);
}
}
4创建新线程
import java.io.DataInputStream;
import java.net.Socket;
public class ClientReaderThread extends Thread{
private Socket socket;
private DataInputStream dis;
private ClientChatFrame win;
public ClientReaderThread( Socket socket, ClientChatFrame win) {
this.win = win;
this.socket = socket;
}
@Override
public void run() {
try {
// 接收的消息可能有很多种类型:1、在线人数更新的数据 2、群聊消息
dis = new DataInputStream(socket.getInputStream());
while (true) {
int type = dis.readInt(); // 1、2、3
switch (type){
case 1:
// 服务端发来的在线人数更新消息
updateClientOnLineUserList();
break;
case 2:
// 服务端发送来的群聊消息
getMsgToWin();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void getMsgToWin() throws Exception {
// 获取群聊消息
String msg = dis.readUTF();
win.setMsgToWin(msg);
}
// 更新客户端的在线用户列表
private void updateClientOnLineUserList() throws Exception {
// 1、读取有多少个在线用户
int count = dis.readInt();
// 2、循环控制读取多少个用户信息。
String[] names = new String[count];
for (int i = 0; i < count; i++) {
// 3、读取每个用户的信息
String nickname = dis.readUTF();
// 4、将每个用户的信息添加到数组
names[i] = nickname;
}
// 5、将集合中的数据展示到窗口上
win.updateOnlineUsers(names);
}
}
5创建一个新的类进行测试
public class App {
public static void main(String[] args) {
//启动程序
new ChatEntryFrame();
}
}