Java在线聊天室课程设计(嘉然UI界面)

一、前言 😆

最近在做JAVA课设,选题是局域网聊天室的实现与设计
忘记了很多知识点,所以整体来说是为了快速应付学校的课设任务

⭐期间借鉴了许多资料,主要知识点:GUI图形化界面、IO输入输出流、网络socket编程⭐

二、效果展示图 🧐

1、服务器界面

在这里插入图片描述

2、客户端登录界面

在这里插入图片描述

3、聊天界面

在这里插入图片描述

三、客户端Client 🙂

1、LoginFrame类

public class LoginFrame extends JFrame {
    // 设置窗体宽度常量
	private static final Integer FRAME_WIDTH = 600;
	// 设置窗体高度常量
	private static final Integer FRAME_HEIGHT = 400;
 		  //构造方法
		public LoginFrame() {
            //下面对LoginFrame的设置加不加this.都行
           	setTitle("❤CHAT ROOM❤");	//设置标题
			setSize(FRAME_WIDTH, FRAME_HEIGHT);	//设置尺寸
			setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);	//设置关闭模式
			setResizable(false);	//窗口是否可缩放(不行)
        
            // 获取屏幕像素并确定计算窗口位置居中,两种方法
			setLocationRelativeTo(null);
				//	Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
				//	int screenWidth = screenSize.width;
				//	int screenheight = screenSize.height;
				//	setLocation((screenWidth - FRAME_WIDTH) / 2, (screenheight - FRAME_HEIGHT) / 2);
            
		  	//标题图标,把咖啡杯换成嘉然的头像
			Toolkit toolkit=Toolkit.getDefaultToolkit();
			Image topIcon=toolkit.getImage("src/image/bg/login-icon.jpg");
			setIconImage(topIcon);
            
           	// 背景图片,把图片放在JLabel标签里
            ImageIcon imageBg = new ImageIcon("src/image/bg/JRbg.jpg");
            JLabel jlbBg = new JLabel(imageBg);
            
            // 图片的位置和大小【不设置也不影响】
            jlbBg.setBounds(0, 0, FRAME_WIDTH, FRAME_HEIGHT);
            jlbBg.setLayout(null);	// 布局为空布局
            add(jlbBg);	//LoginFrame加入标签组件
           	
            // 标题
			JLabel lblTitle = new JLabel("❤ CHAT   ROOM ❤");	
			lblTitle.setBounds(150, 35, 350, 55);	// x,y,宽,高
			lblTitle.setFont(new Font("PingFang SC", Font.BOLD, 36));	//苹方字体,加粗,36尺寸
			lblTitle.setForeground(Color.RED);	//字体红色
			jlbBg.add(lblTitle);	//大背景图加入标题组件
            
            // 账户
            JLabel lblUid = new JLabel("ACCOUNT:");
            lblUid.setBounds(150, 120, 110, 30);	//x,y,宽,高
            lblUid.setFont(new Font("PingFang SC", Font.BOLD, 16));
            lblUid.setForeground(Color.BLACK);
            jlbBg.add(lblUid);
            //账户文本框
            JTextField textUid = new JTextField();
            textUid.setBounds(260, 120, 160, 30);
            jlbBg.add(textUid);
            
            // 密码【和账户设置差不多】
            JLabel lblPwd = new JLabel("PASSWORD:");
            lblPwd.setBounds(150, 180, 110, 30);
            lblPwd.setFont(new Font("PingFang SC", Font.BOLD, 16));
            lblPwd.setForeground(Color.BLACK);
            jlbBg.add(lblPwd);
            //密码文本框
            JPasswordField textPwd = new JPasswordField();
            textPwd.setBounds(260, 180, 160, 30);
            jlbBg.add(textPwd);
            
            // 登录按钮
            JButton login = new JButton("LOGIN");
            login.setBounds(175, 250, 103, 30);
            login.setBackground(Color.PINK);	//背景粉色
            login.setForeground(Color.WHITE);	//字体白色
            login.setFont(new Font("PingFang SC", Font.BOLD, 16));
            
            //登录按钮监听
            login.addActionListener(new ActionListener() {	//匿名内部类,只在登录时使用一次
                @Override
                public void actionPerformed(ActionEvent e) {
                    String username = textUid.getText();	//获取用户名
                    String password = textPwd.getText();	//获取密码
                    TransferInfo transferInfo = new TransferInfo();	//实例化传输信息对象
                    transferInfo.setUsername(username);	//设置用户名
                    transferInfo.setPassword(password);	//设置密码
                    transferInfo.setStatusEnum(ChatStatus.LOGIN);	//设置信息为登录
                    connectionServer(transferInfo);	 //与服务器连接
                }
            });
            jlbBg.add(login); 
            
            // 取消按钮
            JButton cancel = new JButton("CANCEL");
            cancel.setBounds(355, 250, 103, 30);
            cancel.setBackground(Color.PINK);
            cancel.setForeground(Color.WHITE);
            cancel.setFont(new Font("PingFang SC", Font.BOLD, 16));
            jlbBg.add(cancel);
             
            setVisible(true);	//设置可视化
            
     		//连接服务器的方法
            public void connectionServer(TransferInfo transferInfo) {
                try {
                    Socket socket = new Socket("127.0.0.1", 8888);	//创建socket(主机,端口)
                    //写一个消息
                    IOStream.writeMessage(socket, transferInfo);
                    //开启客户端子线程,接收消息
                    ClientHandler clientHandler = new ClientHandler(socket, this);
                    clientHandler.start();

                } catch (UnknownHostException e1) {	//捕获一下异常
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                System.out.println("点击了login按钮");
            }
            
     		//主函数:启动登录界面
            public static void main(String[] args) {
                new LoginFrame();
            }

      }
}

2、ClientHandler类

// 客户端开启一个线程,来一起读消息
public class ClientHandler extends Thread {
	Socket socket;	//声明socket
	LoginFrame loginFrame;	//登录界面
	ChatFrame chatFrame;	//聊天界面
    //含参构造方法(socket,登录界面)
	public ClientHandler(Socket socket, LoginFrame loginFrame) {
		this.socket = socket;
		this.loginFrame = loginFrame;
	}
	@Override
	public void run() {
		while (true) {
			// 模拟一直读消息
			Object object = IOStream.readMessage(socket);
			//如果读到的对象属于传输消息这类
			//if (object instanceof TransferInfo) {
				TransferInfo transferInfo = (TransferInfo) object;
				if (transferInfo.getStatusEnum() == InfoType.LOGIN) {
					//登录消息
					loginResult(transferInfo);
				}else if (transferInfo.getStatusEnum() == InfoType.CHAT) {
					//聊天消息
					chatResult(transferInfo);
					
				}else if (transferInfo.getStatusEnum() == InfoType.NOTICE) {
					//系统消息
					noticeResult(transferInfo);
					
				}else if (transferInfo.getStatusEnum() == InfoType.ULIST) {
					//刷新当前在线用户列表
					onlineUsersResult(transferInfo);
				}
			//}
		}
	}
	/**
	 * 登录消息处理
	 */
	public void loginResult(TransferInfo transferInfo) {
		if (transferInfo.getLoginSuccess()) {
			//登录成功,跳转主页面
			String username = transferInfo.getUsername();
			
			chatFrame = new ChatFrame(username, socket);
			
			//关闭登录窗口
			loginFrame.dispose();

		}else{
			//登录失败
			System.out.println("客户端接收到登录失败");
			System.out.println(transferInfo.getNotice());
		}
	}
	/**
	 * 系统消息处理
	 */
	public void noticeResult(TransferInfo transferInfo) {
		//在公屏上显示系统消息
		String originText = chatFrame.acceptPane.getText();
		if (!originText.isEmpty()) {
			chatFrame.acceptPane.setText(originText + "\n" + transferInfo.getNotice());
		}else{
			chatFrame.acceptPane.setText(transferInfo.getNotice());
		}
		
	}
	/**
	 * 聊天消息处理
	 */
	public void chatResult(TransferInfo transferInfo) {
		String sender = transferInfo.getSender();
		String originText = chatFrame.acceptPane.getText();
		chatFrame.acceptPane.setText(originText + "\n" + "> " + sender + ":" + transferInfo.getContent());
	}
	
	/**
	 * 刷新当前界面的用户列表
	 */
	public void onlineUsersResult(TransferInfo transferInfo) {
		String[] userOnlineArray = transferInfo.getUserOnlineArray();
		
		ImageListModel model = new ImageListModel();
		for (String username: userOnlineArray){
			//头像
			User user = new User();
			user.setUserName(username);
			user.setUiconPath("src/image/" + username + ".jpg");
//			System.out.println(user.getUiconPath());
			user.setMotto("❤然然可爱捏❤");
			model.addElement(user);
		}
		//JList的模型,给我们存放数据
		chatFrame.lstUser.setModel(model);
		//提供给我们自定义想要的皮肤或样式
		chatFrame.lstUser.setCellRenderer(new ImageCellRenderer());
//		chatFrame.lstUser.setListData(userOnlineArray);
	}
}

3、ChatFrame类

//聊天界面
public class ChatFrame extends JFrame {

   private static final long serialVersionUID = 1709267049032639256L;

   // 聊天窗体宽度
   public static final Integer FRAME_WIDTH = 800;

   // 聊天窗体高度
   public static final Integer FRAME_HEIGHT = 700;
   
   //消息接受框
   public JTextPane acceptPane;
   //当前在线用户列表
   public JList lstUser;
   
   String username;
   Socket socket;
   ChatFrame chatFrame;
   JTextPane sendPane;
   JComboBox reciverBox;
   
   public ChatFrame(String username, Socket socket) {
      setTitle("CHAT ROOM");
      this.username = username;
      this.socket = socket;
      chatFrame = this;
      setSize(FRAME_WIDTH, FRAME_HEIGHT);
      setResizable(false);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      //固定在屏幕中间
      setLocationRelativeTo(null);
//    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
//    int width = screenSize.width;
//    int height = screenSize.height;
//    setLocation((width - FRAME_WIDTH) / 2, (height - FRAME_HEIGHT) / 2);

      // 加载窗体的背景图片
      ImageIcon imageIcon = new ImageIcon("src/image/bg/JRbg.jpg");
      JLabel frameBg = new JLabel(imageIcon);
      frameBg.setBounds(0, 0, FRAME_WIDTH, FRAME_HEIGHT);
      add(frameBg);

      //标题图标,把咖啡杯换成嘉然的头像
      Toolkit toolkit=Toolkit.getDefaultToolkit();
      Image topIcon=toolkit.getImage("src/image/bg/chatframe-icon.jpg");
      setIconImage(topIcon);

      // 接收框
      acceptPane = new JTextPane();
      acceptPane.setOpaque(true); // 不透明
      acceptPane.setFont(new Font("PingFang SC",Font.BOLD, 16));

      // 接收滚动条
      JScrollPane scrollPaneOne = new JScrollPane(acceptPane);
      scrollPaneOne.setBounds(225, 20, 500, 332);
      scrollPaneOne.setOpaque(false);
      scrollPaneOne.getViewport().setOpaque(false);
      frameBg.add(scrollPaneOne);

      // 当前在线用户列表
      lstUser = new JList();
      lstUser.setFont(new Font("PingFang SC",Font.BOLD, 14));
      lstUser.setVisibleRowCount(17);
      lstUser.setFixedCellWidth(180);
      lstUser.setFixedCellHeight(60);
      
      JPopupMenu popupMenu = new JPopupMenu();
      JMenuItem privateChat = new JMenuItem("PRIVATE");
      privateChat.addActionListener(new ActionListener() {
         
         @Override
         public void actionPerformed(ActionEvent e) {
            Object reciverObj = lstUser.getSelectedValue();
            if (reciverObj instanceof User) {
               User user = (User) reciverObj;
               String reciver = user.getUserName();
               reciverBox.removeAllItems();
               reciverBox.addItem("ALL");
               reciverBox.addItem(reciver);
               reciverBox.setSelectedItem(reciver);
            }
         }
      });
      popupMenu.add(privateChat);
//    JMenuItem blackList = new JMenuItem("BLACK");
//    blackList.addActionListener(new ActionListener() {
//
//       @Override
//       public void actionPerformed(ActionEvent e) {
//          // TODO Auto-generated method stub
//
//       }
//    });
//    popupMenu.add(blackList);
      //添加点击事件,需要确认是右键。
      lstUser.addMouseListener(new MouseAdapter() {
         @Override
         public void mouseClicked(MouseEvent e) {
            //监听左键|右键
            if (e.isMetaDown() && lstUser.getSelectedIndex() >= 0) {
               //弹出菜单
               popupMenu.show(lstUser, e.getX(), e.getY());
            }
         }
      });
      JScrollPane spUser = new JScrollPane(lstUser);
      spUser.setFont(new Font("PingFang SC", Font.BOLD, 15));
      spUser.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
      spUser.setBounds(15, 17, 200, 507);
      frameBg.add(spUser);

      // 输入框
      sendPane = new JTextPane();
      sendPane.setOpaque(true);
      sendPane.setFont(new Font("PingFang SC", Font.BOLD, 16));

      JScrollPane scoPane = new JScrollPane(sendPane);// 设置滚动条
      scoPane.setBounds(225, 400, 500, 122);
      scoPane.setOpaque(false);
      scoPane.getViewport().setOpaque(false);
      frameBg.add(scoPane);


      //私聊标签
      JLabel chatLabel = new JLabel("CHAT  TO:");
      chatLabel.setBounds(505, 362, 80, 25);
      chatLabel.setFont(new Font("PingFang SC", Font.BOLD, 13));
      frameBg.add(chatLabel);
      //ALL、私聊下拉选择框
      reciverBox = new JComboBox();
      reciverBox.setSelectedItem("ALL");
      reciverBox.addItem("ALL");
      reciverBox.setBounds(580, 362, 150, 25);
      frameBg.add(reciverBox);

      /*
       * 发送按钮
       */
      JButton send = new JButton("SEND");
      send.setBounds(605, 545, 125, 30);
      send.setFont(new Font("PingFang SC", Font.BOLD, 16));
      send.setForeground(Color.WHITE);
      send.setBackground(Color.PINK);
      send.addActionListener(new ActionListener() {
         
         @Override
         public void actionPerformed(ActionEvent e) {
            String content = sendPane.getText();
            TransferInfo transferInfo = new TransferInfo();
            transferInfo.setContent(content);
            //发送人
            transferInfo.setSender(username);
            String reciver = "ALL";
            Object reciverObj = reciverBox.getSelectedItem();
            if (reciverObj != null) {
               reciver = String.valueOf(reciverObj);
            }
            System.out.println("MESSAGE:" + reciver);
            //接收人
            transferInfo.setReciver(reciver);
            transferInfo.setStatusEnum(InfoType.CHAT);
            IOStream.writeMessage(socket, transferInfo);
            sendPane.setText("");
         }
      });
      frameBg.add(send);

      
      //客户端关闭窗体退出
      this.addWindowListener(new WindowAdapter() {
         @Override
         public void windowClosing(WindowEvent e) {
            try {
               System.out.println(username + "CLOSE");
               TransferInfo tfi = new TransferInfo();
               tfi.setStatusEnum(InfoType.EXIT);
               tfi.setUsername(username);
               tfi.setNotice(username + "  LEAVING.....");
               IOStream.writeMessage(socket, tfi);
            } catch (Exception e1) {
               e1.printStackTrace();
            }
         }
      });
       
       //切换背景图片按钮
		ImageIcon alterBtn=new ImageIcon("src/image/bg/alterbtn.jpg");
		JButton alterBg=new JButton(alterBtn);
//		Image temp=alterBtn.getImage().getScaledInstance(alterBg.getWidth(),alterBg.getHeight(),
//				alterBtn.getImage().SCALE_DEFAULT);
//		alterBtn=new ImageIcon(temp);
//		alterBg.setIcon(alterBtn);
		alterBg.setBounds(36, 535, 136, 136);
		alterBg.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				tapCnt++;
				if(tapCnt%3==1){
					frameBg.setIcon(new ImageIcon("src/image/bg/alterBg-1.jpg"));
				}else if(tapCnt%3==2){
					frameBg.setIcon(new ImageIcon("src/image/bg/alterBg-2.jpg"));
				}else{
					frameBg.setIcon(new ImageIcon("src/image/bg/JRbg.jpg"));
				}
			}
		});
		frameBg.add(alterBg);
            
      setVisible(true);

   }
}

四、服务器端Server 🙃

1、ServerFrame类

//服务器界面
public class ServerFrame extends JFrame {
	//随机生成序列号
	private static final long serialVersionUID=-8235369520375669451L;
	//服务器窗体宽度
	public static final Integer FRAME_WIDTH = 600;
	//服务器窗体高度
	public static final Integer FRAME_HEIGHT = 600;
	//声明用户列表【选项卡2】
	public OnlineUserPanel onlineUserPanel;
	//声明服务器信息【选项卡1】
	public ServerInfoPanel serverInfoPanel;
	//构造方法
	public ServerFrame() {

		setTitle("❤SERVER❤");	//标题
		setSize(FRAME_WIDTH, FRAME_HEIGHT);	//窗口大小
		setResizable(false);	//不可缩放窗口

		setLocationRelativeTo(null);	// 窗口居中
//		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
//		int screenWidth = screenSize.width;
//		int screenheight = screenSize.height;
//		setLocation((screenWidth - FRAME_WIDTH) / 2, (screenheight - FRAME_HEIGHT) / 2);

		//标题图标,把咖啡杯换成嘉然的头像
		Toolkit toolkit=Toolkit.getDefaultToolkit();
		Image topIcon=toolkit.getImage("src/image/bg/server-icon.jpg");
		setIconImage(topIcon);

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);		//退出应用程序

		// 选项卡(服务器信息+在线用户面板)
		JTabbedPane tpServer = new JTabbedPane(JTabbedPane.TOP);	//标签位于上方
		tpServer.setBackground(Color.WHITE);	//设置背景颜色
		tpServer.setFont(new Font("PingFang SC",Font.BOLD,20));
		
        //实例化两个选项卡
		serverInfoPanel = new ServerInfoPanel();
		onlineUserPanel = new OnlineUserPanel();
        //选项卡加入服务器信息和在线用户面板
		tpServer.add("SERVER INFO", serverInfoPanel.getServerInfoPanel());	//方法是返回一个面板
		tpServer.add("ONLINE USER", onlineUserPanel.getUserPanel());
		
		add(tpServer);	//服务器界面加入选项卡
		setVisible(true);
	}

}

2、ServerInfoPanel类

//【第一个服务器信息选项卡】
public class ServerInfoPanel {
    
    public JTextField txtNumber;	//人数
	public JTextField txtServerName;	//服务器名称
	public JTextField txtIP;		//IP地址
	public JTextField txtPort;		//端口号
	public JTextPane txtLog;		//日志文本

	public JLabel getServerInfoPanel() {
		// 第一个服务选项卡面板,包括日志区域
		JPanel pnlServer = new JPanel();
		pnlServer.setOpaque(false);
		pnlServer.setLayout(null);
		pnlServer.setBounds(0, 0, ServerFrame.FRAME_WIDTH, ServerFrame.FRAME_HEIGHT);

		// 日志标签
		JLabel lblLog = new JLabel("❤LOG❤");
		lblLog.setForeground(Color.RED);
		lblLog.setFont(new Font("PingFang SC", Font.BOLD, 20));
		lblLog.setBounds(150, 10, 100, 30);
		pnlServer.add(lblLog);	//选项卡加入日志标签组件

		// 日志区域
		txtLog = new JTextPane();
		txtLog.setBackground(Color.BLACK);
		txtLog.setForeground(Color.PINK);
		txtLog.setOpaque(true);
		txtLog.setFont(new Font("PingFang SC", Font.BOLD, 18));
		
		//日志显示屏
		JScrollPane scoPaneOne = new JScrollPane(txtLog);// 设置滚动条,把txtLog塞进去
		scoPaneOne.setBounds(155, 40, 390, 390);
		scoPaneOne.setOpaque(false);
        //设置聊天框与滚动条像素透明,露出下方背景像素
        //【JSCrollPane有两个面板层,一个是他本身,而另外一个则是他的viewport(视口)】
		scoPaneOne.getViewport().setOpaque(false);
		//面板加入日志显示屏
		pnlServer.add(scoPaneOne);
		//停止按钮
		pnlServer.add(stopBtn());
		//保存按钮
		pnlServer.add(saveLogBtn());
		//左边四行参数
		pnlServer.add(getServerParam());

		// 加载窗体的背景图片
		ImageIcon imageIcon = new ImageIcon("src/image/bg/JRbg.jpg");
		// 创建一个标签并将图片添加进去
		JLabel lblBackground = new JLabel(imageIcon);
		// 设置图片的位置和大小
		lblBackground.setBounds(0, 0, 300, 300);
		// 添加到当前窗体中
		lblBackground.add(pnlServer);
		//返回标签背景
		return lblBackground;
	}

	public JPanel getServerParam() {
		// 左边服务器参数信息面板,不包括日志区域
		JPanel serverParamPanel = new JPanel();
		serverParamPanel.setOpaque(false);	//设置透明
		serverParamPanel.setBounds(12, 35, 120, 360);
		serverParamPanel.setFont(new Font("PingFang SC", Font.BOLD, 15));

		//在线人数标签
		JLabel lblNumber = new JLabel("ONLINE:");
		lblNumber.setForeground(Color.ORANGE);
		lblNumber.setFont(new Font("PingFang SC", Font.BOLD, 15));
		serverParamPanel.add(lblNumber);

		//在线人数文本框
		txtNumber = new JTextField("0", 14);
		txtNumber.setFont(new Font("PingFang SC", Font.BOLD, 15));
		txtNumber.setEditable(false);
		txtNumber.setHorizontalAlignment(JTextField.CENTER);	//文本框居中
		serverParamPanel.add(txtNumber);


		//主机名标签
		JLabel lblServerName = new JLabel("NAME:");
		lblServerName.setForeground(Color.ORANGE);
		lblServerName.setFont(new Font("PingFang SC", Font.BOLD, 15));
		serverParamPanel.add(lblServerName);

		//主机名文本框
		txtServerName = new JTextField(14);
		txtServerName.setFont(new Font("PingFang SC", Font.BOLD, 15));
		txtServerName.setEditable(false);
		txtServerName.setHorizontalAlignment(JTextField.CENTER);	//文本框居中
		serverParamPanel.add(txtServerName);


		//IP标签
		JLabel lblIP = new JLabel("IP:");
		lblIP.setForeground(Color.ORANGE);
		lblIP.setFont(new Font("PingFang SC", Font.BOLD, 15));
		serverParamPanel.add(lblIP);

		//IP文本框
		txtIP = new JTextField(14);
		txtIP.setFont(new Font("PingFang SC", Font.BOLD, 15));
		txtIP.setEditable(false);
		txtIP.setHorizontalAlignment(JTextField.CENTER);	//文本框居中
		serverParamPanel.add(txtIP);


		//端口号标签
		JLabel lblPort = new JLabel("PORT:");
		lblPort.setForeground(Color.ORANGE);
		lblPort.setFont(new Font("PingFang SC", Font.BOLD, 15));
		serverParamPanel.add(lblPort);

		//端口号文本框
		txtPort = new JTextField(Port.SERVER_PORT + "", 14);
		txtPort.setFont(new Font("PingFang SC", Font.BOLD, 15));
		txtPort.setEditable(false);	//禁止编辑
		txtPort.setHorizontalAlignment(JTextField.CENTER);	//文本框居中
		serverParamPanel.add(txtPort);

		return serverParamPanel;

	}

	//服务器关闭按钮
	public JButton stopBtn() {
		JButton stopBtn = new JButton("CLOSE");
		stopBtn.setBackground(Color.PINK);
		stopBtn.setForeground(Color.WHITE);
		stopBtn.setFont(new Font("PingFang SC", Font.BOLD, 15));
		stopBtn.setBounds(180, 450, 120, 35);

		return stopBtn;
	}
	//保存日志按钮
	public JButton saveLogBtn() {
		JButton saveLogBtn = new JButton("SAVE");
		saveLogBtn.setBackground(Color.PINK);
		saveLogBtn.setForeground(Color.WHITE);
		saveLogBtn.setFont(new Font("PingFang SC", Font.BOLD, 15));
		saveLogBtn.setBounds(390, 450, 120, 35);

		return saveLogBtn;
	}
}

3、OnlineUserPanel类

//【第二个在线用户选项卡】
public class OnlineUserPanel {
	//用户
	public JList lstUser;
	//在线用户标签
	public JLabel getUserPanel() {

		JPanel pnlUser = new JPanel();
		pnlUser.setLayout(null);	
		pnlUser.setBackground(Color.PINK);
		pnlUser.setBounds(30, 5, 480, 400);
		pnlUser.setOpaque(false);// 设置透明

		JLabel lblUser = new JLabel("❤ ONLINE  LIST ❤");
		lblUser.setFont(new Font("PingFang SC", Font.BOLD, 20));
		lblUser.setBounds(20, 10, 200, 30);
		lblUser.setForeground(Color.PINK);
		pnlUser.add(lblUser);	//标题标签加到大面板

		// 用户列表
		lstUser = new JList();
		lstUser.setFont(new Font("PingFang SC", Font.BOLD, 16));
		lstUser.setVisibleRowCount(17);		//设置列表可见行数
		lstUser.setFixedCellWidth(180);		//单元宽度
		lstUser.setFixedCellHeight(35);		//单元高度
		lstUser.setOpaque(false);

		JScrollPane spUser = new JScrollPane(lstUser);	//实例化含用户列表的滚动面板
		spUser.setFont(new Font("PingFang SC", Font.BOLD, 14));
		spUser.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		spUser.setBounds(30, 45, 440, 360);
		spUser.setOpaque(false);
		pnlUser.add(spUser);	//滚动面板加到大面版

		// 加载窗体的背景图片
		ImageIcon imageIcon = new ImageIcon("src/image/bg/JRbg.jpg");
		// 创建一个标签并将图片添加进去
		JLabel lblBackground = new JLabel(imageIcon);
		// 设置图片的位置和大小
		lblBackground.setBounds(0, 200, 300, 300);
		// 添加到当前窗体中
		lblBackground.add(pnlUser);
		//返回大面板
		return lblBackground;
	}
}

4、ServerHandler类

//服务器启动一个线程,一直读消息
public class ServerHandler extends Thread {
	
   Socket socket;	
   ServerFrame serverFrame;

   public ServerHandler(Socket socket, ServerFrame serverFrame) {
      this.socket = socket;
      this.serverFrame = serverFrame;
   }
   
   static ArrayList<String> onlineUsers = new ArrayList<>();	
   static ArrayList<Socket> onlineSockets = new ArrayList<>();

   @Override
   public void run() {

      // 默认重复拿
      while (true) {
         // 模拟一直拿消息
         Object object = IOStream.readMessage(socket);		//从socket当中读取消息
         System.out.println("Server receive:" + object);

        // if (object instanceof TransferInfo) {		//如果读取的对象是TransferInfo的实例化
            TransferInfo transferInfo = (TransferInfo) object;	//将对象强整形
            if (transferInfo.getStatusEnum() == InfoType.LOGIN) {
               //登录消息类型
               loginHandler(transferInfo);
            }else if (transferInfo.getStatusEnum() == InfoType.CHAT) {
               //聊天信息类型
               chatHandler(transferInfo);
            }else if (transferInfo.getStatusEnum() == InfoType.EXIT) {
               //退出聊天类型
               logout(transferInfo);
               try {
                  Thread.sleep(1000);
                  socket.close();    //关闭socket连接
               } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
               } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
               }
               this.interrupt();//关闭当前线程
               break;
            }
        // }
      }
   }
   
   //客户端登录处理
   public void loginHandler(TransferInfo transferInfo) {
      boolean flag = checkUserLogin(transferInfo);
      transferInfo.setLoginSuccess(false);
      if (flag) {
         //返回登录成功给客户端(登录消息)
         transferInfo.setLoginSuccess(true);
         transferInfo.setStatusEnum(InfoType.LOGIN);
         IOStream.writeMessage(socket, transferInfo);
         String username = transferInfo.getUsername();
         
         //统计在线人数
         onlineUsers.add(username);
         onlineSockets.add(socket);
         ChatServer.userSocketMap.put(username, socket);
         
         //返回用户上线信息给所有用户(系统消息)
         transferInfo = new TransferInfo();
         transferInfo.setStatusEnum(InfoType.NOTICE);
         String notice = ">>  ❤ Welcome " + username + " ❤";
         transferInfo.setNotice(notice);
         sendAll(transferInfo); 
         
         //发送最新的用户列表给客户端
         transferInfo = new TransferInfo();
         transferInfo.setUserOnlineArray(onlineUsers.toArray(new String[onlineUsers.size()]));
         transferInfo.setStatusEnum(InfoType.ULIST);
         sendAll(transferInfo);
         
         //刷新在线用户列表
         flushOnlineUserList();
         
         //发送信息给服务器日志
         log(notice);
      }else{
         //返回登录失败给客户端
         IOStream.writeMessage(socket, transferInfo);
         log(transferInfo.getUsername() + "LOGIN FAILED!");
      }
   }
   public void log(String log) {
      Date date = new Date();
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      String dateStr = simpleDateFormat.format(date);
      JTextPane txtLog = serverFrame.serverInfoPanel.txtLog;
      String oldLog = txtLog.getText();
      txtLog.setText(oldLog + "\n" + dateStr + ":" + log);
   }

    //刷新用户列表(当一个用户上线时,便刷新)
   public void flushOnlineUserList() {
      JList lstUser = serverFrame.onlineUserPanel.lstUser;
      String[] userArray = onlineUsers.toArray(new String[onlineUsers.size()]);
      lstUser.setListData(userArray);
      serverFrame.serverInfoPanel.txtNumber.setText(userArray.length + "");
   }

    //广播给所有人
   public void sendAll(TransferInfo transferInfo) {
      for (int i = 0; i < onlineSockets.size(); i++){
         Socket tempSocket = onlineSockets.get(i);
         IOStream.writeMessage(tempSocket, transferInfo);
      }
   }

    //私聊
   public void send(TransferInfo transferInfo) {
      String reciver = transferInfo.getReciver();
      String sender = transferInfo.getSender();
      Socket socket1 = ChatServer.userSocketMap.get(reciver);
      IOStream.writeMessage(socket1, transferInfo);
      
      Socket socket2 = ChatServer.userSocketMap.get(sender);
      IOStream.writeMessage(socket2, transferInfo);
   }

    // 处理客户端聊天请求
   public void chatHandler(TransferInfo transferInfo) {
      String reciver = transferInfo.getReciver();
      if ("ALL".equals(reciver)) {
         //发送给所有人
         sendAll(transferInfo);
      }else{
         //私聊
         send(transferInfo);
      }
   }
   
   //验证登录
   public boolean checkUserLogin(TransferInfo transferInfo) {
      try {
         String username = transferInfo.getUsername();
         String password = transferInfo.getPassword();
         FileInputStream fileInputStream = new FileInputStream(new File("src/user.txt"));
         DataInputStream dataInputStream = new DataInputStream(fileInputStream);
         String row = null;
         while ((row = dataInputStream.readLine()) != null) {
            //从文件中读取的行
            String fileUser = row;
            if ((username + "@" + password).equals(row)) {
               System.out.println("Name and Password are correct!");
               return true;
            }else{
               System.out.println("Login failed!");
            }
         }
      } catch (FileNotFoundException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (IOException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   
      return false;
   }

   //用户退出处理,清理在线人数,刷新用户列表,告诉所有人,你已经离开

   private void logout(TransferInfo transferInfo) {
      String userName = transferInfo.getUsername();
      //将该用户从用户集合移除
      Iterator<String> userIter = onlineUsers.iterator();
      while(userIter.hasNext()) {
         if(userIter.next().equals(userName)) {
            userIter.remove();
         }
      }
      
      //将该用户从socket集合移除
      Iterator<Socket> socketIter = onlineSockets.iterator();
      while(socketIter.hasNext()) {
         Socket next = socketIter.next();
         if(socket == next) {
            socketIter.remove();
         }
      }
      
      //将user与Socket的关系从Map中移除
      ChatServer.userSocketMap.remove(userName);
      
      //刷新服务器面板的用户列表
      flushOnlineUserList();
      
      //给所有在线的用户发送下线消息
      transferInfo.setStatusEnum(InfoType.NOTICE);
      sendAll(transferInfo);
      
      //告诉其他人刷新用户列表
      transferInfo.setUserOnlineArray(onlineUsers.toArray(new String [onlineUsers.size()]));
      transferInfo.setStatusEnum(InfoType.ULIST);
      sendAll(transferInfo);
   }

}

5、ChatServer类

//服务器启动
public class ChatServer {
   static HashMap<String, Socket> userSocketMap = new HashMap<>();

   public ServerFrame serverFrame;

   public ChatServer() {
      try{
         //建立服务器Socket监听 
         ServerSocket serverSocket = new ServerSocket(Port.SERVER_PORT);    //端口号:8888
         //打开服务器界面
         serverFrame = new ServerFrame();
         ServerInfoBean serverInfo = getServerIP();
         loadServerInfo(serverInfo);
         //解决多客户端连接的循环
         while(true){
            //等待连接,阻塞实现,会得到一个客户端连接
            Socket socket = serverSocket.accept();
            //启动一个线程来一直接收消息
            ServerHandler serverHandler = new ServerHandler(socket, serverFrame);
            serverHandler.start();
            
            System.out.println("CONNECTED:" + socket);
         }
         
      }catch (IOException e) {
         e.printStackTrace();
      }
      
   }
   
    //获取服务器的主机名和IP地址,返回服务器IP等信息
   public ServerInfoBean getServerIP() {
      ServerInfoBean sib = null;
      try {
         InetAddress serverAddress = InetAddress.getLocalHost();
         byte[] ipAddress = serverAddress.getAddress();
         sib = new ServerInfoBean();
         sib.setIp(serverAddress.getHostAddress());
         sib.setHostName(serverAddress.getHostName());
         sib.setPort(Port.SERVER_PORT);

         System.out.println("Server IP is:" + (ipAddress[0] & 0xff) + "."
               + (ipAddress[1] & 0xff) + "." + (ipAddress[2] & 0xff) + "."
               + (ipAddress[3] & 0xff));
      } catch (Exception e) {
         System.out.println("CouLd not get Server IP." + e);
      }
      return sib;
   }
   public void loadServerInfo(ServerInfoBean serverInfo) {
      serverFrame.serverInfoPanel.txtIP.setText(serverInfo.getIp());
      serverFrame.serverInfoPanel.txtServerName.setText(serverInfo.getHostName());
      serverFrame.serverInfoPanel.txtLog.setText("❤ SERVER SUCCESS ❤");
      
   }

   //主函数
   public static void main(String[] args) {
      new ChatServer();
   }
}

五、常量Constants 😳

1、Port 端口

随便设置,只要不被占用即可😉

public class Port {

	//服务器端口8888
	public final static Integer SERVER_PORT = 8888;
}

六、输入输出流IO 😋

1、IOStream类

public class IOStream {
	//读消息
	public static Object readMessage(Socket socket) {	
		Object object = null;
		try {
			InputStream inputStream = socket.getInputStream();
			ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
			object = objectInputStream.readObject();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return object; //返回读到的对象
	}
	//写消息
	public static void writeMessage(Socket socket, Object message) {
		try {
			OutputStream outputStream = socket.getOutputStream();
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
			objectOutputStream.writeObject(message);
			objectOutputStream.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

七、实体类entity 😬

1、InfoType消息类型类


public enum InfoType {
   
   LOGIN(1, "登录"),
   NOTICE(2, "系统消息"),
   CHAT(3, "聊天消息"),
   ULIST(4, "在线用户列表"),
   EXIT(5, "退出");
   
   private Integer status;
   private String desc;
   private InfoType(int status, String desc) {
      this.status = status;
      this.desc = desc;
   }
   public Integer getStatus() {
      return status;
   }
   public void setStatus(Integer status) {
      this.status = status;
   }
   public String getDesc() {
      return desc;
   }
   public void setDesc(String desc) {
      this.desc = desc;
   }
   
}

2、ServerInfoBean 服务器信息类


public class ServerInfoBean implements Serializable {

   private static final long serialVersionUID = 4800399407766443229L;

   private String hostName;
   private String ip;
   private Integer port;
   public String getHostName() {
      return hostName;
   }
   public void setHostName(String hostName) {
      this.hostName = hostName;
   }
   public String getIp() {
      return ip;
   }
   public void setIp(String ip) {
      this.ip = ip;
   }
   public Integer getPort() {
      return port;
   }
   public void setPort(Integer port) {
      this.port = port;
   }

}

3、TransferInfo 传输消息类

package com.xy.chatting.entity;

import java.io.Serializable;

public class TransferInfo implements Serializable {

   private static final long serialVersionUID = -8501366066504250232L;
   private String username;         //用户名
   private String password;         //密码
   private String content;             //聊天内容
   private Boolean loginSuccess;     //登录成功标志
   private String notice;          //系统消息
   private InfoType statusEnum;      //消息类型
   private String[] userOnlineArray;  //在线用户列表
   private String sender;          //消息发送人
   private String reciver;             //消息接收者
   
   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 Boolean getLoginSuccess() {
      return loginSuccess;
   }
   public void setLoginSuccess(Boolean loginSuccess) {
      this.loginSuccess = loginSuccess;
   }
   public String getNotice() {
      return notice;
   }
   public void setNotice(String notice) {
      this.notice = notice;
   }
   public InfoType getStatusEnum() {
      return statusEnum;
   }
   public void setStatusEnum(InfoType statusEnum) {
      this.statusEnum = statusEnum;
   }
   public String[] getUserOnlineArray() {
      return userOnlineArray;
   }
   public void setUserOnlineArray(String[] userOnlineArray) {
      this.userOnlineArray = userOnlineArray;
   }
   public String getContent() {
      return content;
   }
   public void setContent(String content) {
      this.content = content;
   }
   public String getSender() {
      return sender;
   }
   public void setSender(String sender) {
      this.sender = sender;
   }
   public String getReciver() {
      return reciver;
   }
   public void setReciver(String reciver) {
      this.reciver = reciver;
   }
   
}

4、User 用户类

public class User implements Serializable{

   private static final long serialVersionUID = 5126205470509431771L;
   
   private String userName;
   private String motto;
// private String nickName;
   private String uiconPath;
   public String getUserName() {
      return userName;
   }
   public void setUserName(String userName) {
      this.userName = userName;
   }
   public String getMotto() {
      return motto;
   }
   public void setMotto(String motto) {
      this.motto = motto;
   }
// public String getNickName() {
//    return nickName;
   }
// public void setNickName(String nickName) {
//    this.nickName = nickName;
// }
   public String getUiconPath() {
      return uiconPath;
   }
   public void setUiconPath(String uiconPath) {
      this.uiconPath = uiconPath;
   }
}

八、Ulist列表美化 😝

1、ImageCellRenderer 自定义JList渲染器

public class ImageCellRenderer extends DefaultListCellRenderer {
   
   private static final long serialVersionUID = -6233481640415228808L;

   @Override
   public Component getListCellRendererComponent(JList<?> list, Object value, 
         int index, boolean isSelected, boolean cellHasFocus) {
      
      if(value instanceof User) {
         User user = (User) value;
         String uiconPath = user.getUiconPath();
         String userName = user.getUserName();
         String motto = user.getMotto();

         ImageIcon icon = new ImageIcon(uiconPath);
         icon.setImage(icon.getImage().getScaledInstance(55, 55, Image.SCALE_DEFAULT));
         setIcon(icon);
         String text = "<html><body><span color='#00B8D8' style='font-size:16px;'>" + userName + "</span><br/>" + motto + "</body></html>";
         setText(text);
         if (isSelected) {        //当某个元素被选中时
            setBackground(Color.LIGHT_GRAY);        //设置背景色
         }else {
            setForeground(Color.GRAY);
         }
         setVerticalTextPosition(SwingConstants.TOP);
         setHorizontalTextPosition(SwingConstants.RIGHT);
      }
      return this;
   }
}

2、ImageListModel JList模型类

package com.xy.chatting.ulist;

import java.util.List;
import java.util.ArrayList;

import javax.swing.AbstractListModel;

import com.xy.chatting.entity.User;

/**
 * JList的模型类
 */
public class ImageListModel extends AbstractListModel<User> {

   private static final long serialVersionUID = 8116378668465150072L;
   
   
   private ArrayList<User> list = new ArrayList<User>();
   
   public void addElement(User str) {

      list.add(str);
   }
   @Override
   public User getElementAt(int index) {
   
      return list.get(index);
   }

   @Override
   public int getSize() {
      // TODO Auto-generated method stub
      return list.size();
   }

}

九、整个聊天室写完辣 🤗

  • 14
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值