多人聊天室
多人聊天室需要从客户端和服务器端两个方面着手来实现
客户端
客户端分为登录界面,聊天界面
登录界面
package com.ffyc.chatRoom;
import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.Socket;
import java.util.regex.Pattern;
public class LoginFrame extends JFrame {
public LoginFrame() throws HeadlessException{
this.setSize(600,400);//设置窗体的高和宽
this.setLocationRelativeTo(null); //设置窗体水平垂直居中
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置窗体关闭后程序结束
this.setTitle("欢迎登录!");
JPanel jPanel = new JPanel(new GridLayout(4,1));
JPanel welcomePanel = new JPanel(new FlowLayout());
JLabel welcomeLabel = new JLabel("欢迎登录!");
welcomeLabel.setFont(new Font("宋体",Font.BOLD,35));
welcomeLabel.setForeground(new Color(7, 255, 224));
welcomePanel.add(welcomeLabel);
JPanel accountPanel = new JPanel(new FlowLayout());
JLabel accountLabel = new JLabel("账号:");
accountLabel.setFont(new Font("宋体",Font.BOLD,25));
accountLabel.setForeground(new Color(248, 3, 3));
JTextField accountTextField = new JTextField(15);
accountPanel.add(accountLabel);
accountPanel.add(accountTextField);
JPanel passwordPanel = new JPanel(new FlowLayout());
JLabel passwordLable = new JLabel("密码:");
passwordLable.setFont(new Font("宋体",Font.BOLD,25));
passwordLable.setForeground(new Color(255, 3, 3));
JPasswordField passwordField = new JPasswordField(15);
passwordPanel.add(passwordLable);
passwordPanel.add(passwordField);
JPanel buttonPanel = new JPanel(new FlowLayout());
JButton loginButton = new JButton("登录");
JButton regButton = new JButton("注册");
buttonPanel.add(loginButton);
buttonPanel.add(regButton);
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String account = accountTextField.getText();
String password = new String(passwordField.getPassword());
if (account.length() == 0){
JOptionPane.showMessageDialog(null, "账号不能为空,请重新输入!",
"系统提示!",JOptionPane.WARNING_MESSAGE);
return;
}
if (password.length() == 0){
JOptionPane.showMessageDialog(null,"密码不能为空!");
return;
}
String pattern = "^\\w+$";
boolean flag = Pattern.matches(pattern,password) && Pattern.matches(pattern,account);
if (!flag){
JOptionPane.showMessageDialog(null,"账号密码只能由数字、字母组成!请重新输入!");
return;
}
//后期预留与数据库交互
//创建Socket
try {
Socket socket = new Socket("127.0.0.1",9999);
new ChatFrame(account,socket);//创建聊天窗口
dispose();//关闭登录界面
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null, "服务器连接失败,请稍后再试!");
}
}
});
jPanel.add(welcomePanel);
jPanel.add(accountPanel);
jPanel.add(passwordPanel);
jPanel.add(buttonPanel);
this.add(jPanel);
this.setVisible(true);
}
}聊天界面
package com.ffyc.chatRoom;
import util.DateUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class ChatFrame extends JFrame {
JTextArea showArea;
public ChatFrame(String account, Socket socket) throws HeadlessException, IOException {
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
this.setSize(600,500);//设置聊天窗口的高和宽
this.setLocationRelativeTo(null);//设置窗体水平垂直居中
//this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗体关闭程序结束
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.setTitle("欢迎来到"+account+"聊天室");
this.setResizable(false);//设置禁止窗口拖拽调整大小
//最大面板 便捷布局
JPanel jPanel = new JPanel(new BorderLayout());
//创建一个文本域 用来显示多行聊天信息
showArea = new JTextArea(23,34);
showArea.setLineWrap(true);
//创建一个滚动面板组件,将文本框作为显示组件
JScrollPane scrollPane = new JScrollPane(showArea);
showArea.setEditable(false);//设置文本域为不可编辑
JPanel sendPanel = new JPanel(new FlowLayout());
//创建一个文本框,用来输入单行聊天信息
JTextArea inputField = new JTextArea(3,20);
JButton sendbutton = new JButton("发送");
inputField.setLineWrap(true);
JScrollPane inputPanel = new JScrollPane(inputField);
//为发送按钮添加监听事件
sendbutton.addActionListener(e -> {
String content = inputField.getText();
//判断输入的信息是否为空
//trim()方法返回调用字符串对象的一个副本,但是所有起始和结尾的空格都被删除了
if (content != null && !content.trim().equals("")){
//如果不为空,将向服务器端发送此消息
String msg = account + " "+ DateUtil.dateToString("yyyy-MM-dd HH:mm:ss\n");
msg += content;
System.out.println(msg);
try {
dataOutputStream.writeUTF(msg);
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null,"内容发送失败,请检查网络!");
}
}else {
//如果为空 则提示聊天信息不能为空
JOptionPane.showMessageDialog(null,"发送内容不能为空!");
}
inputField.setText("");//将输入的文本框设置为空
});
JLabel messagelabel = new JLabel("聊天信息");//创建一个标签
sendPanel.add(messagelabel);
sendPanel.add(inputPanel);
sendPanel.add(sendbutton);
jPanel.add(scrollPane,BorderLayout.NORTH);
jPanel.add(sendPanel,BorderLayout.SOUTH);
this.add(jPanel);
this.setVisible(true);
//来到聊天窗口后,可以开启一个线程监听服务器端发送的消息
new ClientThread(socket).start();
//为窗口添加事件监听
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int res = JOptionPane.showConfirmDialog(null, "你确定要退出聊天吗?",
"操作提示",JOptionPane.OK_CANCEL_OPTION);
if (res==0){
//点击确定
new LoginFrame();//打开登录窗口
dispose();
}
}
});
}
//线程监听服务器发送的消息
class ClientThread extends Thread{
boolean mark = true;
DataInputStream dataInputStream;
public ClientThread(Socket socket) throws IOException {
dataInputStream = new DataInputStream(socket.getInputStream());
}
@Override
public void run() {
while (mark){
try {
String msg = dataInputStream.readUTF();
showArea.append(msg+"\n");//显示聊天内容,追加保留了之前的内容
} catch (IOException e) {
e.printStackTrace();
mark = false;
}
}
}
}
}
主要的客户端界面到这里就基本成型了,这里还需要用到我们之前自己写的until包里面的日期转字符串构造方法,如下:
package util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
//日期转字符串
public class DateUtil {
public static String dateToString(Date date ,String p){
SimpleDateFormat sdf = new SimpleDateFormat(p);
String str = sdf.format(date);
return str;
}
public static String dateToString(String p){
SimpleDateFormat sdf = new SimpleDateFormat(p);
String str =sdf.format(new Date());
return str;
}
//字符串转日期
public static Date stringToDate(String datestr,String p) throws ParseException {
SimpleDateFormat sdf =new SimpleDateFormat(p);
Date date =sdf.parse(datestr);
return date;
}
public static void main(String[] args) {
System.out.println(DateUtil.dateToString("yyyy-MM-dd"));
}
}最后我们在run类里面启动客户端即可
package com.ffyc.chatRoom;
public class Run {
public static void main(String[] args) {
new LoginFrame();
}
}
服务器端
package com.ffyc.chatroomserver;
import javax.swing.*;
import java.awt.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Server extends JFrame {
JTextArea serverText;
public Server(){
this.setSize(500,500);//设置聊天窗口的高和宽
this.setLocationRelativeTo(null);//设置窗体水平垂直居中
//this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗体关闭程序结束
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.setTitle("聊天室服务器端监控");
this.setResizable(false);//设置禁止窗口拖拽调整大小
//最大面板 便捷布局
JPanel jPanel = new JPanel(new BorderLayout());
//创建一个文本域 用来显示多行聊天信息
serverText = new JTextArea();
serverText.setLineWrap(true);
//创建一个滚动面板组件,将文本框作为显示组件
JScrollPane scrollPane = new JScrollPane(serverText);
serverText.setEditable(false);//设置文本域为不可编辑
jPanel.add(scrollPane);
JPanel sendPanel = new JPanel(new FlowLayout());
//创建一个文本框,用来输入单行聊天信息
JTextArea inputField = new JTextArea(3,20);
JButton sendbutton = new JButton("发送公告");
inputField.setLineWrap(true);
JScrollPane inputPanel = new JScrollPane(inputField);
sendPanel.add(inputPanel);
sendPanel.add(sendbutton);
jPanel.add(sendPanel,BorderLayout.SOUTH);
this.add(jPanel);
this.setVisible(true);
startServer();
}
//创建一个用来存放所有连接服务器端的Socket集合
ArrayList<Socket> sockets = new ArrayList<>();
//启动服务器
public void startServer(){
try {
ServerSocket serverSocket = new ServerSocket(9999);
serverText.append("服务器启动成功!\n");
//监听多个客户端连接
while (true){
//监听客户端连接
Socket socket = serverSocket.accept();
sockets.add(socket);//将连接服务器的socket存进集合中
serverText.append("有客户端连接成功,连接数为:"+sockets.size()+"\n");
//为每一个连接到服务器的客户端开启一个线程
new SocketThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
serverText.append("服务器启动失败!\n");
}
}
//创建一个内部类,继承Thread类,用来监听自己客户端有没有发消息
class SocketThread extends Thread{
boolean flag = true;
DataInputStream dataInputStream;
Socket socket;
public SocketThread(Socket socket) throws IOException {
this.socket = socket;
this.dataInputStream = new DataInputStream(socket.getInputStream());
}
@Override
public void run() {
while (flag){//一直死循环,监听客户端发送的消息
try {
String msg = dataInputStream.readUTF();
serverText.append(msg+"\n");
//向不同的客户端转发消息
//遍历socket集合
for (Socket soc : sockets){
DataOutputStream dataOutputStream = new DataOutputStream(soc.getOutputStream());
dataOutputStream.writeUTF(msg);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端下线了");
serverText.append("客户端下线了!");
sockets.remove(socket);
flag = false;
}
}
}
}
}最后我们在服务器RUN类里面启动服务器即可
package com.ffyc.chatroomserver;
public class ServerRun {
public static void main(String[] args) {
new Server();
}
}
运行效果
运行效果如下: