Java在线聊天室(客户端+服务器)

article/2025/10/22 9:55:35

Java在线聊天室(客户端+服务器)基于webSocket实现模拟在线聊天系统

运行环境: JDK1.8version

开发环境:Eclipse

一、结构

以下是系统主要结构:

1.用户(均实现部分可能会有bug)
(1)自定义用户名登录
(2)端口修改灵活使用服务器端口
(3)断开服务器功能
(4)可查询在线用户列表
(5)可对在线列表用户进行私聊操作(自测)
(6)群发消息在线用户都可以看见消息

client代码参考如下

public class Client{private JFrame frame;public JList userList;public int i;public  String soloId;private JTextArea textArea;private JTextArea systemTextArea;private JTextField textField;private JTextField txt_port;private JTextField txt_hostIp;private JTextField txt_name;
//	private JComboBox select;
//	private JList jList;  private JButton btn_start;private JButton btn_stop;private JButton btn_send;private JPanel northPanel;private JPanel southPanel;private JScrollPane rightScroll;private JScrollPane leftScroll;private JSplitPane centerSplit;private DefaultListModel listModel;private boolean isConnected = false;private Socket socket;private PrintWriter writer;private BufferedReader reader;private MessageThread messageThread;// 负责接收消息的线程private Map<String, User> onLineUsers = new HashMap<String, User>();// 所有在线用户public boolean isConnected() {return isConnected;}public void setConnected(boolean isConnected) {this.isConnected = isConnected;}// 主方法,程序入口public static void main(String[] args) {new Client();}// 执行发送public void send(String c) {if (!isConnected) {JOptionPane.showMessageDialog(frame, "还没有连接服务器,无法发送消息!", "错误",JOptionPane.ERROR_MESSAGE);return;}String message = textField.getText().trim();if (message == null || message.equals("")) {JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误",JOptionPane.ERROR_MESSAGE);return;}if (isConnected == false) {JOptionPane.showMessageDialog(frame, "您已被強制下线!", "错误",JOptionPane.ERROR_MESSAGE);return;}if(c=="all") {sendMessage(frame.getTitle() + "@" + "ALL" + "@" + message+"@"+ soloId);textField.setText(null);return;}if(c=="solo") {System.out.println(soloId+"soloId");sendMessage(frame.getTitle() + "@" + "SOLO" + "@" + message + "@" + soloId);textField.setText(null);return;}}// 构造方法public Client() {textArea = new JTextArea();systemTextArea = new JTextArea();textArea.setEditable(false);textArea.setForeground(Color.BLACK);systemTextArea.setEditable(false);systemTextArea.setForeground(Color.blue);textField = new JTextField();txt_port = new JTextField(8);txt_port.setText("8888");//设置大小txt_hostIp = new JTextField(8);txt_hostIp.setText("127.0.0.1");txt_name = new JTextField(8);txt_name.setText("xiaohei");btn_start = new JButton("连接");btn_start.setPreferredSize(new Dimension(120,10));btn_stop = new JButton("断开");btn_stop.setPreferredSize(new Dimension(120,10));btn_send = new JButton("发送");btn_send.setPreferredSize(new Dimension(120,10));listModel = new DefaultListModel();userList = new JList(listModel);northPanel = new JPanel(new FlowLayout(FlowLayout.LEADING,10,15));
//		northPanel.setLayout(new GridLayout(1, 7));Box bnorthPanel=Box.createHorizontalBox();    //创建横向Box容器northPanel.add(bnorthPanel);bnorthPanel.add(new JLabel("姓名:"));bnorthPanel.add(Box.createHorizontalStrut(20));bnorthPanel.add(txt_name);bnorthPanel.add(Box.createHorizontalStrut(150));bnorthPanel.add(new JLabel("服务器IP:"));bnorthPanel.add(Box.createHorizontalStrut(20));bnorthPanel.add(txt_hostIp);bnorthPanel.add(Box.createHorizontalStrut(130));bnorthPanel.add(new JLabel("端口:"));bnorthPanel.add(Box.createHorizontalStrut(20));bnorthPanel.add(txt_port);bnorthPanel.add(Box.createHorizontalStrut(30));bnorthPanel.add(btn_start);bnorthPanel.add(Box.createHorizontalStrut(10));bnorthPanel.add(btn_stop);northPanel.setBorder(new TitledBorder("连接信息"));rightScroll = new JScrollPane(textArea);textArea.add(systemTextArea);rightScroll.setBorder(new TitledBorder("消息显示区"));leftScroll = new JScrollPane(userList);leftScroll.setBorder(new TitledBorder("在线用户"));southPanel = new JPanel(new BorderLayout());southPanel.add(textField, "Center");
//		southPanel.add(select,"Center");southPanel.add(btn_send, "East");southPanel.setBorder(new TitledBorder("写消息"));centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll,rightScroll);centerSplit.setDividerLocation(100);frame = new JFrame("客户机");// 更改JFrame的图标:
//		frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png")));frame.setLayout(new BorderLayout());frame.add(northPanel, "North");frame.add(centerSplit, "Center");frame.add(southPanel, "South");frame.setSize(1050, 500);int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;frame.setLocation((screen_width - frame.getWidth()) / 2,(screen_height - frame.getHeight()) / 2);frame.setVisible(true);// 写消息的文本框中按回车键时事件textField.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent arg0) {send("all");}});//单击在线用户事件userList.addMouseListener(new MouseListener() {@Overridepublic void mouseClicked(MouseEvent e) {// TODO Auto-generated method stubint i = userList.getSelectedIndex();soloId = (String) listModel.get(i);send("solo");
//				for (int i = listModel.size() - 1; i >= 0; i--) {
//					
//					System.out.println(listModel.get(i)+"111111111111");
//				}
//				new SeparateChat();}@Overridepublic void mousePressed(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseReleased(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseEntered(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseExited(MouseEvent e) {// TODO Auto-generated method stub}});// 单击发送按钮时事件btn_send.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {send("all");}});// 单击连接按钮时事件btn_start.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {int port;	if (isConnected) {JOptionPane.showMessageDialog(frame, "已处于连接上状态,不要重复连接!","错误", JOptionPane.ERROR_MESSAGE);return;}try {try {port = Integer.parseInt(txt_port.getText().trim());} catch (NumberFormatException e2) {throw new Exception("端口号不符合要求!端口为整数!");}String hostIp = txt_hostIp.getText().trim();String name = txt_name.getText().trim();if (name.equals("") || hostIp.equals("")) {throw new Exception("姓名、服务器IP不能为空!");}//					boolean flag = connectServer(port, hostIp, name);if (flag == false) {throw new Exception("与服务器连接失败!");}frame.setTitle(name);JOptionPane.showMessageDialog(frame, "成功连接!");} catch (Exception exc) {JOptionPane.showMessageDialog(frame, exc.getMessage(),"错误", JOptionPane.ERROR_MESSAGE);}}});// 单击断开按钮时事件btn_stop.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {if (!isConnected) {JOptionPane.showMessageDialog(frame, "已处于断开状态,不要重复断开!","错误", JOptionPane.ERROR_MESSAGE);return;}try {boolean flag = closeConnection();// 断开连接if (flag == false) {throw new Exception("断开连接发生异常!");}JOptionPane.showMessageDialog(frame, "成功断开!");} catch (Exception exc) {JOptionPane.showMessageDialog(frame, exc.getMessage(),"错误", JOptionPane.ERROR_MESSAGE);}}});// 关闭窗口时事件frame.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {if (isConnected) {closeConnection();// 关闭连接}System.exit(0);// 退出程序}});}/*** 连接服务器* * @param port* @param hostIp* @param name*/public boolean connectServer(int port, String hostIp, String name) {// 连接服务器try {socket = new Socket(hostIp, port);// 根据端口号和服务器ip建立连接writer = new PrintWriter(socket.getOutputStream());reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 发送客户端用户基本信息(用户名和ip地址)sendMessage(name + "@" + socket.getLocalAddress().toString());// 开启接收消息的线程messageThread = new MessageThread(reader, textArea);messageThread.start();isConnected = true;// 已经连接上了return true;} catch (Exception e) {textArea.append("【系统消息】与端口号为:" + port + "    IP地址为:" + hostIp+ "   的服务器连接失败!" + "\r\n");isConnected = false;// 未连接上return false;}}/*** 发送消息* * @param message*/public void sendMessage(String message) {writer.println(message);writer.flush();}/*** 客户端主动关闭连接*/@SuppressWarnings("deprecation")public synchronized boolean closeConnection() {try {sendMessage("CLOSE");// 发送断开连接命令给服务器listModel.removeAllElements();messageThread.stop();// 停止接受消息线程// 释放资源if (reader != null) {reader.close();}if (writer != null) {writer.close();}if (socket != null) {socket.close();}isConnected = false;return true;} catch (IOException e1) {e1.printStackTrace();isConnected = true;return false;}}// 不断接收消息的线程class MessageThread extends Thread {private BufferedReader reader;private JTextArea textArea;// 接收消息线程的构造方法public MessageThread(BufferedReader reader, JTextArea textArea) {this.reader = reader;this.textArea = textArea;}// 被动的关闭连接public synchronized void closeCon() throws Exception {// 清空用户列表listModel.removeAllElements();// 被动的关闭连接释放资源if (reader != null) {reader.close();}if (writer != null) {writer.close();}if (socket != null) {socket.close();}isConnected = false;// 修改状态为断开}public void run() {String message = "";while (true) {try {message = reader.readLine();StringTokenizer stringTokenizer = new StringTokenizer(message, "/@");String command = stringTokenizer.nextToken();// 命令System.out.println(command);if (command.equals("CLOSE"))// 服务器已关闭命令{textArea.append("【系统消息】服务器已关闭!\r\n");closeCon();// 被动的关闭连接return;// 结束线程}else if(command.equals("KickCLOSE")) {closeCon();// 被动的关闭连接return;// 结束线程}else if(command.equals("NAME")) {int size = Integer.parseInt(stringTokenizer.nextToken());String username = null;String userIp = null;String thisName = txt_name.getText().trim();for (int i = 0; i < size; i++) {username = stringTokenizer.nextToken();userIp = stringTokenizer.nextToken();
//							User user = new User(username, userIp);if(username.equals(thisName)) {
//								isConnected = false;sendMessage("CLOSE");// 发送断开连接命令给服务器closeCon();// 被动的关闭连接textArea.append("【系统消息】与端口号为:" + txt_port.getText().trim() + "    IP地址为:" + txt_hostIp.getText().trim()+ "   的服务器连接失败!" + "\r\n");JOptionPane.showMessageDialog(frame, "该姓名已被占用!", "错误",JOptionPane.ERROR_MESSAGE);return;}}//						closeCon();// 被动的关闭连接
//						JOptionPane.showMessageDialog(frame, "该姓名已被占用!", "错误",
//								JOptionPane.ERROR_MESSAGE);
//						return;// 结束线程}else if (command.equals("ADD")) {// 有用户上线更新在线列表String username = "";String userIp = "";if ((username = stringTokenizer.nextToken()) != null&& (userIp = stringTokenizer.nextToken()) != null) {User user = new User(username, userIp);onLineUsers.put(username, user);listModel.addElement(username);}} else if (command.equals("DELETE")) {// 有用户下线更新在线列表String username = stringTokenizer.nextToken();User user = (User) onLineUsers.get(username);onLineUsers.remove(user);listModel.removeElement(username);} else if (command.equals("USERLIST")) {// 加载在线用户列表int size = Integer.parseInt(stringTokenizer.nextToken());String username = null;String userIp = null;for (int i = 0; i < size; i++) {username = stringTokenizer.nextToken();userIp = stringTokenizer.nextToken();User user = new User(username, userIp);onLineUsers.put(username, user);listModel.addElement(username);}} else if (command.equals("MAX")) {// 人数已达上限textArea.append(stringTokenizer.nextToken()+ stringTokenizer.nextToken() + "\r\n");closeCon();// 被动的关闭连接JOptionPane.showMessageDialog(frame, "服务器缓冲区已满!", "错误",JOptionPane.ERROR_MESSAGE);return;// 结束线程}else {// 普通消息textArea.append(message + "\r\n");}} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}}}
}

2.服务器(全部已实现)
(1)指定端口开启服务器
(2)设置服务器上线人数
(3)停止服务器响应
(4)查询在线用户列表
(5)群发消息
(6)对指定用户进行移除聊天室

server代码

public class Server {private JFrame frame;private JTextArea contentArea;private JTextField txt_message;private JTextField txt_max;private JTextField txt_port;private JTextField kickOutUser;private JButton btn_start;private JButton btn_stop;private JButton btn_send;private JButton btn_kick;private JPanel northPanel;private JPanel southPanel;private JPanel eastPanel;private JPanel eastUpPanel;private JPanel eastDownPanel;private JPanel westPanel;private JScrollPane rightPanel;private JScrollPane leftPanel;private JSplitPane centerSplit;private JList userList;private DefaultListModel listModel;private ServerSocket serverSocket;private ServerThread serverThread;private ArrayList<ClientThread> clients;private boolean isStart = false;private Client client;//	Client client = new Client();// 主方法,程序执行入口public static void main(String[] args) {new Server();	}// 执行消息发送public void send() {if (!isStart) {JOptionPane.showMessageDialog(frame, "服务器还未启动,不能发送消息!", "错误",JOptionPane.ERROR_MESSAGE);return;}if (clients.size() == 0) {JOptionPane.showMessageDialog(frame, "没有用户在线,不能发送消息!", "错误",JOptionPane.ERROR_MESSAGE);return;}String message = txt_message.getText().trim();if (message == null || message.equals("")) {JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误",JOptionPane.ERROR_MESSAGE);return;}SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式String Time = df.format(new Date());sendServerMessage(message);// 群发服务器消息contentArea.append(Time + "  " + "服务器说:" + txt_message.getText() + "\r\n");txt_message.setText(null);}// 构造放法public Server() {
//		client = new Client();frame = new JFrame("服务器");// 更改JFrame的图标://frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png")));
//		frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Server.class.getResource("/KeTang/WebContent/img/bg.jpg")));contentArea = new JTextArea();contentArea.setEditable(false);contentArea.setForeground(Color.black);txt_message = new JTextField();txt_max = new JTextField(8);txt_max.setText("6");txt_port = new JTextField(8);txt_port.setText("8888");kickOutUser = new JTextField(8);kickOutUser.setText("xiaohei");btn_start = new JButton("启动");btn_start.setPreferredSize(new Dimension(120,10));btn_stop = new JButton("停止");btn_stop.setPreferredSize(new Dimension(120,10));btn_send = new JButton("发送");btn_send.setPreferredSize(new Dimension(120,10));btn_kick = new JButton("踢");btn_stop.setEnabled(false);listModel = new DefaultListModel();userList = new JList(listModel);southPanel = new JPanel(new BorderLayout());southPanel.setBorder(new TitledBorder("写消息"));southPanel.add(txt_message, "Center");southPanel.add(btn_send, "East");leftPanel = new JScrollPane(userList);leftPanel.setBorder(new TitledBorder("在线用户"));rightPanel = new JScrollPane(contentArea);rightPanel.setBorder(new TitledBorder("消息显示区"));centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel,rightPanel);centerSplit.setDividerLocation(100);northPanel = new JPanel(new FlowLayout(FlowLayout.LEADING,10,15));
//		northPanel.setLayout(new GridLayout(1, 6));Box bnorthPanel=Box.createHorizontalBox();    //创建横向Box容器northPanel.add(bnorthPanel);bnorthPanel.add(new JLabel("人数上限:"));bnorthPanel.add(Box.createHorizontalStrut(20));bnorthPanel.add(txt_max);bnorthPanel.add(Box.createHorizontalStrut(150));bnorthPanel.add(new JLabel("端口:"));bnorthPanel.add(Box.createHorizontalStrut(20));bnorthPanel.add(txt_port);bnorthPanel.add(Box.createHorizontalStrut(30));bnorthPanel.add(btn_start);bnorthPanel.add(Box.createHorizontalStrut(10));bnorthPanel.add(btn_stop);northPanel.setBorder(new TitledBorder("配置信息"));eastUpPanel = new JPanel(new FlowLayout(FlowLayout.LEADING,3,15));eastUpPanel.add(new JLabel("被踢用户:"));eastUpPanel.add(kickOutUser);eastUpPanel.add(btn_kick);eastDownPanel = new JPanel();Box beastDownPanel=Box.createVerticalBox();    //创建纵向Box容器eastDownPanel.add(beastDownPanel);beastDownPanel.add(Box.createVerticalStrut(150));beastDownPanel.add(new JLabel("未完待续..."));eastPanel = new JPanel(new BorderLayout());eastPanel.add(eastUpPanel,"North");eastPanel.add(eastDownPanel,"Center");eastPanel.setBorder(new TitledBorder("服务器功能操作"));westPanel = new JPanel(new BorderLayout());westPanel.add(northPanel,"North");westPanel.add(centerSplit, "Center");westPanel.add(southPanel, "South");frame.setLayout(new BorderLayout());frame.add(westPanel, "West");frame.add(eastPanel, "East");frame.setSize(1050, 500);//frame.setSize(Toolkit.getDefaultToolkit().getScreenSize());//设置全屏int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;frame.setLocation((screen_width - frame.getWidth()) / 2,(screen_height - frame.getHeight()) / 2);frame.setVisible(true);// 关闭窗口时事件frame.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {if (isStart) {closeServer();// 关闭服务器}System.exit(0);// 退出程序}});//单击踢出用户btn_kick.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent arg0) {String a = kickOutUser.getText().trim();kickOutUser(a);}});// 文本框按回车键时事件txt_message.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {send();}});// 单击发送按钮时事件btn_send.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent arg0) {send();}});// 单击启动服务器按钮时事件btn_start.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {if (isStart) {JOptionPane.showMessageDialog(frame, "服务器已处于启动状态,不要重复启动!","错误", JOptionPane.ERROR_MESSAGE);return;}int max;int port;try {try {max = Integer.parseInt(txt_max.getText());} catch (Exception e1) {throw new Exception("人数上限为正整数!");}if (max <= 0) {throw new Exception("人数上限为正整数!");}try {port = Integer.parseInt(txt_port.getText());} catch (Exception e1) {throw new Exception("端口号为正整数!");}if (port <= 0) {throw new Exception("端口号 为正整数!");}serverStart(max, port);contentArea.append("服务器已成功启动!人数上限:" + max + ",端口:" + port+ "\r\n");JOptionPane.showMessageDialog(frame, "服务器成功启动!");btn_start.setEnabled(false);txt_max.setEnabled(false);txt_port.setEnabled(false);btn_stop.setEnabled(true);} catch (Exception exc) {JOptionPane.showMessageDialog(frame, exc.getMessage(),"错误", JOptionPane.ERROR_MESSAGE);}}});// 单击停止服务器按钮时事件btn_stop.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {if (!isStart) {JOptionPane.showMessageDialog(frame, "服务器还未启动,无需停止!", "错误",JOptionPane.ERROR_MESSAGE);return;}try {closeServer();btn_start.setEnabled(true);txt_max.setEnabled(true);txt_port.setEnabled(true);btn_stop.setEnabled(false);contentArea.append("服务器成功停止!\r\n");JOptionPane.showMessageDialog(frame, "服务器成功停止!");} catch (Exception exc) {JOptionPane.showMessageDialog(frame, "停止服务器发生异常!", "错误",JOptionPane.ERROR_MESSAGE);}}});}// 踢出用户方法	public void kickOutUser(String kickOutUser){User user = new User(kickOutUser, kickOutUser);boolean exist = false;SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式String Time = df.format(new Date());//当前是否有启动服务器if (!isStart) {JOptionPane.showMessageDialog(frame, "服务器还未启动,不能踢出!", "错误",JOptionPane.ERROR_MESSAGE);return;}//当前是否有用户if (clients.size() == 0) {JOptionPane.showMessageDialog(frame, "没有用户在线,踢出失败!", "错误",JOptionPane.ERROR_MESSAGE);return;}for (int i = clients.size() - 1; i >= 0; i--) {ClientThread temp = clients.get(i);if (clients.get(i).getUser().getName().equals(kickOutUser)) {//服务器窗口显示该用户强制下线消息contentArea.append(Time+"  "+clients.get(i).getUser().getName()+ clients.get(i).getUser().getIp() + "被强制下线!\r\n");//给该用户发送强制下线消息clients.get(i).getWriter().println("【系统消息】用户"+clients.get(i).getUser().getName()+"您已被强制下线!\r\n");clients.get(i).getWriter().flush();// 更新在线列表listModel.removeElement(clients.get(i).getUser().getName());clients.get(i).getWriter().println("KickCLOSE");clients.get(i).getWriter().flush();clients.remove(i);// 删除此用户的服务线程temp.stop();// 停止这条服务线程exist = true;//						client.setConnected(false);}}if (exist == false) {JOptionPane.showMessageDialog(frame, "该用户不在线,踢出失败!", "错误",JOptionPane.ERROR_MESSAGE);}for (int i = clients.size() - 1; i >= 0; i--) {// 向所有在线用户发送该用户的下线命令clients.get(i).getWriter().println("DELETE@" + user.getName());clients.get(i).getWriter().println("【系统消息】用户"+user.getName()+"被强制下线!\r\n");clients.get(i).getWriter().flush();}}// 启动服务器public void serverStart(int max, int port) throws java.net.BindException {try {clients = new ArrayList<ClientThread>();serverSocket = new ServerSocket(port);serverThread = new ServerThread(serverSocket, max);serverThread.start();isStart = true;} catch (BindException e) {isStart = false;throw new BindException("端口号已被占用,请换一个!");} catch (Exception e1) {e1.printStackTrace();isStart = false;throw new BindException("启动服务器异常!");}}// 关闭服务器@SuppressWarnings("deprecation")public void closeServer() {try {if (serverThread != null)serverThread.stop();// 停止服务器线程for (int i = clients.size() - 1; i >= 0; i--) {// 给所有在线用户发送关闭命令clients.get(i).getWriter().println("CLOSE");clients.get(i).getWriter().flush();// 释放资源clients.get(i).stop();// 停止此条为客户端服务的线程clients.get(i).reader.close();clients.get(i).writer.close();clients.get(i).socket.close();clients.remove(i);}if (serverSocket != null) {serverSocket.close();// 关闭服务器端连接}listModel.removeAllElements();// 清空用户列表isStart = false;} catch (IOException e) {e.printStackTrace();isStart = true;}}// 群发服务器消息public void sendServerMessage(String message) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式String Time = df.format(new Date());for (int i = clients.size() - 1; i >= 0; i--) {clients.get(i).getWriter().println("【系统消息】"+"服务器:" + message);clients.get(i).getWriter().flush();}}// 服务器线程class ServerThread extends Thread {private ServerSocket serverSocket;private int max;// 人数上限// 服务器线程的构造方法public ServerThread(ServerSocket serverSocket, int max) {this.serverSocket = serverSocket;this.max = max;}public synchronized void run() {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式String Time = df.format(new Date());while (true) {// 不停的等待客户端的链接try {Socket socket = serverSocket.accept();if (clients.size() == max) {// 如果已达人数上限BufferedReader r = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter w = new PrintWriter(socket.getOutputStream());// 接收客户端的基本用户信息String inf = r.readLine();StringTokenizer st = new StringTokenizer(inf, "@");User user = new User(st.nextToken(), st.nextToken());// 反馈连接成功信息w.println("MAX@【系统消息】服务器:对不起," + user.getName()+ user.getIp() + ",服务器在线人数已达上限,请稍后尝试连接!");w.flush();// 释放资源r.close();w.close();socket.close();continue;}ClientThread client = new ClientThread(socket);client.start();// 开启对此客户端服务的线程clients.add(client);listModel.addElement(client.getUser().getName());// 更新在线列表contentArea.append(Time+"  "+client.getUser().getName()+ client.getUser().getIp() + "上线了!\r\n");} catch (IOException e) {e.printStackTrace();}}}}// 为一个客户端服务的线程class ClientThread extends Thread {private Socket socket;private BufferedReader reader;private PrintWriter writer;private User user;public BufferedReader getReader() {return reader;}public PrintWriter getWriter() {return writer;}public User getUser() {return user;}// 客户端线程的构造方法public ClientThread(Socket socket) {try {this.socket = socket;reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));writer = new PrintWriter(socket.getOutputStream());// 接收客户端的基本用户信息String inf = reader.readLine();StringTokenizer st = new StringTokenizer(inf, "@");user = new User(st.nextToken(), st.nextToken());String temp = "";SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式String Time = df.format(new Date());// 反馈连接成功信息writer.println("【系统消息】" + user.getName() + user.getIp() + "与服务器连接成功!");writer.flush();// 反馈当前在线用户信息if (clients.size() > 0) {for (int i = clients.size() - 1; i >= 0; i--) {temp += (clients.get(i).getUser().getName() + "/" + clients.get(i).getUser().getIp())+ "@";if(clients.get(i).getUser().getName().equals(user.getName())) {writer.println("NAME@" + clients.size() + "@" + temp);//判断姓名是否重复writer.flush();}}writer.println("USERLIST@" + clients.size() + "@" + temp);writer.flush();}// 向所有在线用户发送该用户上线命令for (int i = clients.size() - 1; i >= 0; i--) {clients.get(i).getWriter().println("ADD@" + user.getName() + user.getIp());
//					clients.get(i).getWriter().flush();
//					clients.get(i).getWriter().println(
//							"【系统消息】用户"+user.getName()+"上线了!\r\n");clients.get(i).getWriter().flush();}for (int i = clients.size() - 1; i >= 0; i--) {clients.get(i).getWriter().println("【系统消息】用户"+user.getName()+"上线了!\r\n");clients.get(i).getWriter().flush();}} catch (IOException e) {e.printStackTrace();}}@SuppressWarnings("deprecation")public void run() {// 不断接收客户端的消息,进行处理。String message = null;SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式String Time = df.format(new Date());while (true) {try {message = reader.readLine();// 接收客户端消息if (message.equals("CLOSE"))// 下线命令{contentArea.append(Time+"  "+this.getUser().getName()+ this.getUser().getIp() + "下线了!\r\n");// 断开连接释放资源reader.close();writer.close();socket.close();// 向所有在线用户发送该用户的下线命令for (int i = clients.size() - 1; i >= 0; i--) {clients.get(i).getWriter().println("DELETE@" + user.getName());clients.get(i).getWriter().flush();}for (int i = clients.size() - 1; i >= 0; i--) {clients.get(i).getWriter().println("【系统消息】用户"+user.getName()+"下线了!\r\n");clients.get(i).getWriter().flush();}listModel.removeElement(user.getName());// 更新在线列表// 删除此条客户端服务线程for (int i = clients.size() - 1; i >= 0; i--) {if (clients.get(i).getUser() == user) {ClientThread temp = clients.get(i);clients.remove(i);// 删除此用户的服务线程temp.stop();// 停止这条服务线程return;}}}else {dispatcherMessage(message);// 多人转发消息}} catch (IOException e) {e.printStackTrace();}}}// 转发消息public void dispatcherMessage(String message) {StringTokenizer stringTokenizer = new StringTokenizer(message, "@");String source = stringTokenizer.nextToken();String owner = stringTokenizer.nextToken();String content = stringTokenizer.nextToken();String soloId = stringTokenizer.nextToken();SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式String Time = df.format(new Date());if (owner.equals("ALL")) {// 群发message =Time + "  " + source + ":" + content;contentArea.append(message + "\r\n");for (int i = clients.size() - 1; i >= 0; i--) {clients.get(i).getWriter().println(message);clients.get(i).getWriter().flush();}} else if (owner.equals("SOLO")) {message =Time + "  " + source + "对" + soloId+ ":" + content;contentArea.append(message + "\r\n");for (int i = clients.size() - 1; i >= 0; i--) {if (clients.get(i).getUser().getName().equals(user.getName())) {//当前用户一对一发送message =Time + "  " + "你对" + soloId + ":" + content;clients.get(i).getWriter().println(message);clients.get(i).getWriter().flush();}if (clients.get(i).getUser().getName().equals(soloId)) {//一对一发送给soloIdmessage =Time + "  " +source + "对你:" + content;clients.get(i).getWriter().println(message);clients.get(i).getWriter().flush();}}}}
//		public void dispatcherSoloMessage(String message) {
//			StringTokenizer stringTokenizer = new StringTokenizer(message, "@");
//			String source = stringTokenizer.nextToken();
//			String owner = stringTokenizer.nextToken();
//			String content = stringTokenizer.nextToken();
//			String soloId = stringTokenizer.nextToken();
//			message = source + ":" + content;
//			contentArea.append(message + "\r\n");
//			
				String soloString = client.soloId;
				System.out.println("2222222222");
				System.out.println(soloString+"11111111111");
				for (int i = clients.size() - 1; i >= 0; i--) {
					if(clients.get(i).getName().equals(soloString)) {
					clients.get(i).getWriter().println(message + "(单独发送)");
					clients.get(i).getWriter().flush();
			}
//		
//		}}
}

代码仅供参考~
附上运行截图:
在这里插入图片描述
在这里插入图片描述

获取项目:点击这里


http://chatgpt.dhexx.cn/article/5n7D6fMO.shtml

相关文章

apiDay09整理笔记(笔记优化)

目录 1.聊天室&#xff08;续&#xff09; &#xff08;1&#xff09;实现服务端发送消息给客户端 &#xff08;2&#xff09;服务端转发消息给所有客户端 &#xff08;3&#xff09;客户端解决收发消息的冲突问题 &#xff08;4&#xff09;服务端完成处理客户端断开连接…

JAVA简单聊天室的实现

目录 界面效果图 一、聊天室功能介绍&#xff1f; 二、功能代码 1.服务端 2.客户端 界面效果图 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、聊天室功能介绍&#xff1f; 1. 、对于聊天室就是处理多个客户端发送的请求与信息&#xff0c;从…

Java网络聊天室---个人博客

Java网络聊天室 ———个人博客 一、项目简介 功能描述&#xff1a; 使用图形用户界面和socket通信&#xff0c;能实现一个聊天室中多人聊天&#xff0c;可以两人私聊&#xff0c;可以发送文件。 实现类似QQ用户注册、登录、聊天等功能。 参考git地址或博客地址&#xff1a…

Java实现ChatRoom

基于连接通信Socket、多线程的Java聊天室 1、开发环境&#xff1a; IDEA2018.1JDK1.8 2、实现功能&#xff1a; 实现了模拟登录注册、群聊、私聊、显示当前在线人数列表&#xff1b; 在发送信息时&#xff0c;会向对方发送者及显示发送时间&#xff1b; 显示在线人数列表…

Java聊天室

项目介绍&#xff1a; Java聊天室是期末设计&#xff0c; 阿里巴巴druidmysql多线程GUImvn项目java Socket 服务端模块&#xff1a;踢出聊天室&#xff1a;管理员可以踢出发言不当的用户。只有当开启服务端的时候&#xff0c;客户端才能起到作用。 客户端模块&#xff1a; 注册…

用Java实现简易聊天室

说明&#xff1a;如果一个 类&#xff0c;需要有界面的显示&#xff0c;那么该类就需要继承自JFrame&#xff0c;此时&#xff0c;该类就可以被称为一个“窗体类"。 服务端代码&#xff1a; package cn.qy.chat;import javax.swing.*; import java.awt.*; import java.aw…

微信小程序购物车功能实现(干货满满)

微信小程序定制好看的购物车页面&#xff0c;实现购物车功能&#xff0c;希望对您有所帮助&#xff01; 1. 应用场景 2. 思路分析 3. 代码分析 4. 具体实现代码 效果截图&#xff1a; 1.应用场景 适用于商城、秒杀、商品购买等类型的小程序&#xff0c;负责将顾客浏览的商…

微信小程序微商城(八):缓存实现商品购物车功能

IT实战联盟博客&#xff1a;http://blog.100boot.cn 上一篇&#xff1a;微信小程序微商城&#xff08;七&#xff09;&#xff1a;动态API实现商品分类 看效果 购物车.gif 开发计划 1、商品详情页将商品信息放入缓存 2、购物车页面读取缓存获取商品信息 3、购物车商品计算…

微信小程序开发一个小型商城(六、购物车页面)

上一篇文章&#xff1a;微信小程序开发一个小型商城&#xff08;五、商品详情&#xff09; 当我们在商品详情界面中点击添加购物后&#xff0c;会跳转到购物车界面&#xff0c;购物车界面是一个tabbar&#xff0c;在跳转的时候需要加上ope-type。看下购物车的静态页面把&#x…

微信小程序实现一个购物车页面的简易列表效果

本文只是简单的模仿天猫APP的购物车列表的样式效果&#xff0c;并实现了部分事件功能&#xff0c;功能并不完善&#xff0c;请降低期待观看。 天猫APP的购物车效果&#xff1a; 小程序模仿的实现效果&#xff1a; wxml部分的代码&#xff1a; <view wx:if"{{!isCartEmp…

【Python之pymysql库学习】一、分析fetchone()、fetchmany()、fetchall()(保姆级图文+实现代码)

目录 实现效果实现思路实现代码总结 欢迎关注 『Python之pymysql库学习』 系列&#xff0c;持续更新中 欢迎关注 『Python之pymysql库学习』 系列&#xff0c;持续更新中 实现效果 实现思路 其实有半数代码是创建数据库和创建数据表并插入数据这些环境配置部分我都写好了&…

fetchone、fetchall

fetchone(): 该方法获取下一个查询结果集。结果集是一个对象,读取一行结果&#xff0c;读取完指向下一行&#xff0c;到空为止 fetchall():接收全部的返回结果行&#xff0c;到空为止 fetchone() &#xff1a; 返回单个的元组&#xff0c;也就是一条记录(row)&#xff0c;如果没…

python fetchall方法_Python连接MySQL并使用fetchall()方法过滤特殊字符

python3.3从mysql里取出的数据莫名其妙有括号和逗号每天跟自己喜欢的人一起&#xff0c;通电话&#xff0c;旅行&#xff0c;重复一个承诺和梦想&#xff0c;听他第二十八次提起童年往事&#xff0c;每年的同一天和他庆祝生日&#xff0c;每年的情人节圣诞节除夕&#xff0c;也…

pdo fetchAll

作用 fetchAll()方法是获取结果集中的所有行.其返回值是一个包含结果集中所有数据的二维数组。 PDOStatement::fetchAll ([ int $fetch_style [, mixed $fetch_argument[, array$ctor_args array() ]]] ) fetch_style:控制结果的返回方式 PDO::FETCH_ASSOC 关联数组形式 PD…

记录一个常用函数fetchall()的使用过程

fetchall() 作用是返回多个元组&#xff0c;即对应数据库里的多条数据概念&#xff1b; 常见用法是 cursor.execute(‘select * from table’) value cursor.fetchall() 此时&#xff0c;print(value)则会输出以下二维元组&#xff0c;如下图 拓展&#xff1a; 同类函数fet…

Python从Oracle数据库中获取数据——fetchall(),fetchone(),fetchmany()函数功能分析

Python从Oracle数据库中获取数据——fetchall(),fetchone(),fetchmany()函数功能分析 一、fetchall()&#xff0c;fetchone()&#xff0c;fetchmany()简单介绍 1、fetchall()函数,它的返回值是多个元组,即返回多个行记录,如果没有结果,返回的是() 2、fetchone()函数,它的返回…

KITTI数据集可视化(一):点云多种视图的可视化实现

如有错误&#xff0c;恳请指出。 在本地上&#xff0c;可以安装一些软件&#xff0c;比如&#xff1a;Meshlab&#xff0c;CloudCompare等3D查看工具来对点云进行可视化。而这篇博客是将介绍一些代码工具将KITTI数据集进行可视化操作&#xff0c;包括点云鸟瞰图&#xff0c;FOV…

KITTI数据集的点云格式转PCD格式

参考文章&#xff1a;https://blog.csdn.net/xinguihu/article/details/78922005 KITTI数据集应该不用多做介绍了&#xff0c;基本上做自动驾驶的都知道这个东西。最近本人用到这个数据集想看看里面的点云长什么模样&#xff0c;却发现有点别扭&#xff0c;没有直接可以看的工…

使用kitti数据集实现自动驾驶——发布照片、点云、IMU、GPS、显示2D和3D侦测框

本次内容主要是使用kitti数据集来可视化kitti车上一些传感器&#xff08;相机、激光雷达、IMU&#xff09;采集的资料以及对行人和车辆进行检测并在图像中画出行人和车辆的2D框、在点云中画出行人和车辆的3D框。 首先先看看最终实现的效果&#xff1a; 自动驾驶视频 看了上面的…

KITTI数据集-label解析笔记

笔记摘自&#xff1a;KITTI数据集--label解析与传感器间坐标转换参数解析_苏源流的博客-CSDN博客 KITTI数据集是自动驾驶领域最知名的数据集之一。 一、kitti数据集&#xff0c;label解析 16个数代表的含义&#xff1a; 第1个字符串&#xff1a;代表目标的类别 Car, Van, Tru…