文章目录
- 一、前言 😆
- 二、效果展示图 🧐
- 1、服务器界面
- 2、客户端登录界面
- 3、聊天界面
- 三、客户端Client 🙂
- 1、LoginFrame类
- 2、ClientHandler类
- 3、ChatFrame类
- 四、服务器端Server 🙃
- 1、ServerFrame类
- 2、ServerInfoPanel类
- 3、OnlineUserPanel类
- 4、ServerHandler类
- 5、ChatServer类
- 五、常量Constants 😳
- 1、Port 端口
- 六、输入输出流IO 😋
- 1、IOStream类
- 七、实体类entity 😬
- 1、InfoType消息类型类
- 2、ServerInfoBean 服务器信息类
- 3、TransferInfo 传输消息类
- 4、User 用户类
- 八、Ulist列表美化 😝
- 1、ImageCellRenderer 自定义JList渲染器
- 2、ImageListModel JList模型类
- 九、整个聊天室写完辣 🤗
一、前言 😆
最近在做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() { //匿名内部类,只在登录时使用一次@Overridepublic 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 blocke1.printStackTrace();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}System.out.println("点击了login按钮");}//主函数:启动登录界面public static void main(String[] args) {new LoginFrame();}}
}
2、ClientHandler类
// 客户端开启一个线程,来一起读消息
public class ClientHandler extends Thread {Socket socket; //声明socketLoginFrame loginFrame; //登录界面ChatFrame chatFrame; //聊天界面//含参构造方法(socket,登录界面)public ClientHandler(Socket socket, LoginFrame loginFrame) {this.socket = socket;this.loginFrame = loginFrame;}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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<>();@Overridepublic 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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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 {//服务器端口8888public 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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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 blocke.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;@Overridepublic 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);}@Overridepublic User getElementAt(int index) {return list.get(index);}@Overridepublic int getSize() {// TODO Auto-generated method stubreturn list.size();}}