项目总结:这次项目主要是根据视频来的,结果跟到一半感觉跟不上,慢慢自己有了自己的想法,决定自己先不看学习视频,自己先试着写。
总结写前面,算是写的第一个项目吧。项目中遇到几点问题,首先Scoket对象创建后,服务器端和客户端不能同时创建输入流,否者会引起堵塞。
然后,读入流应该重新创建个线程做等待写入服务,因为读入流会引起当前线程进入阻塞状态。
还有一个用户线程对应一个服务线程,不是多个用户线程对应一个服务线程。
对对象的操作应该由那个对象本身提供操作方法,比如操作UI界面的变化应该由界面本身提拱。
最后最重要的是写代码之前应该先画个流程图,写代码时参数乱传的,哪里需要就调参数过来。导致思路不清.
首先是需求分析:
本次项目是模拟及时通信中最基本的功能,类似QQ的应用.
项目分为:
(1)服务器端:
服务器端主要负责用户管理,消息转发功能
(2) 客户端:
客户端主要负责用户间的消息发送
详细设计
服务器端
1、登录服务器后,进行客户端的监听,如有客户端连接,启动用户服务线程,与客户端进行交互。
2、如客户端向所有人发送消息,服务器将向所有在线用户广播该消息。
3、如客户端向指定人发送消息,服务器将查找接收人用户线程后转发消息。
4、用户登录后,向所有人更新用户列表。
客户端:
1、用户登录功能。
2、登录后用户可以看到在线用户列表
3 、向指定人发送消息
4、向所有人发送消息
代码的实现
首先,先构建界面图形出来
上面左边是用户界面(服务器界面和用户界面一样),右边是登录界面
由于全用手写代码比较麻烦,我用了可视化UI插件;
画出来如下图
界面做出来了,然后构建对象模型,
这里主要需要信息对象,和用户对象;
信息对象又分,登录信息,发送信息和退出信息。这里我把登录信息单独用一个对象构建,因为保存了帐号密码,以后好增加登录验证功能。
然后就是逻辑功能的实现了。
这里我发现看似简单的功能,实现起来还是有点麻烦的。学会了一点就是要模块化。不然自己很容易搞迷糊。
直接上代码:
客户端代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.Client; 2 3 import java.awt.Toolkit; 4 5 public class ClientFrame { 6 7 private JFrame Clientframe; 8 private JTextField textField; 9 private String username; 10 private JTextArea textArea = null; 11 private JList<String> list = null; 12 private DefaultListModel<String> model = null; 13 private UserService us; 14 15 // 初始化用户列表 16 public void InitUserList() { 17 model = new DefaultListModel<String>(); 18 model.addElement("所有人"); 19 list.setModel(model); 20 } 21 22 // 添加用户到在线列表 23 public void AddUserToList(String username) { 24 // 拆了东墙补西墙。。。之前添加的要全删了,再添加一次 25 model.removeAllElements(); 26 model.addElement("所有人"); 27 String[] str = username.split(","); 28 for (String s : str) { 29 model.addElement(s); 30 } 31 //list.setModel(model); 32 } 33 34 public void updateText(String text) { 35 StringBuffer sb = new StringBuffer(); 36 sb.append(textArea.getText()).append(DateUtil.getTime() + "\n").append(text).append("\n"); 37 textArea.setText(sb.toString()); 38 } 39 public void DelUser(String username){ 40 model.removeElement(username); 41 } 42 43 /** 44 * Create the application. 45 */ 46 public ClientFrame(String username,UserService us) { 47 this.username = username; 48 this.us=us; 49 initialize(); 50 // 初始化用户列表 51 InitUserList(); 52 } 53 54 /** 55 * Initialize the contents of the frame. 56 */ 57 private void initialize() { 58 Clientframe = new JFrame(); 59 Clientframe.addWindowListener(new WindowAdapter() { 60 @Override 61 public void windowClosing(WindowEvent e) { 62 Info info=new Info(); 63 info.setFromUser(username); 64 info.setInfoType(EnumInfoType.EXIT); 65 us.send(info); 66 us.setFlag(false); 67 Clientframe.dispose(); 68 } 69 }); 70 Clientframe.setVisible(true); 71 Clientframe 72 .setIconImage(Toolkit.getDefaultToolkit().getImage(ClientFrame.class.getResource("/com/gh/res/1.png"))); 73 Clientframe.setTitle("\u804A\u804A-\u5BA2\u6237\u7AEF"); 74 Toolkit tk = Toolkit.getDefaultToolkit(); 75 Dimension d = tk.getScreenSize(); 76 int x = (int) (d.getWidth() - 534) / 2; 77 int y = (int) (d.getHeight() - 383) / 2; 78 Clientframe.setBounds(x, y, 534, 383); 79 Clientframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 80 Clientframe.getContentPane().setLayout(new BorderLayout(0, 0)); 81 82 JLabel label = new JLabel("当前用户是:" + username); 83 Clientframe.getContentPane().add(label, BorderLayout.NORTH); 84 85 JPanel jpanel = new JPanel(); 86 Clientframe.getContentPane().add(jpanel, BorderLayout.EAST); 87 jpanel.setLayout(new BorderLayout(0, 0)); 88 89 JLabel lblNewLabel = new JLabel("--\u5728\u7EBF\u7528\u6237\u5217\u8868--"); 90 jpanel.add(lblNewLabel, BorderLayout.NORTH); 91 92 list = new JList<String>(); 93 jpanel.add(list, BorderLayout.CENTER); 94 95 JScrollPane scrollPane = new JScrollPane(); 96 Clientframe.getContentPane().add(scrollPane, BorderLayout.CENTER); 97 98 textArea = new JTextArea(); 99 scrollPane.setViewportView(textArea); 100 101 JPanel panel = new JPanel(); 102 Clientframe.getContentPane().add(panel, BorderLayout.SOUTH); 103 panel.setLayout(new BorderLayout(0, 0)); 104 105 JLabel lblNewLabel_1 = new JLabel("\u8BF7\u8F93\u5165\uFF1A"); 106 panel.add(lblNewLabel_1, BorderLayout.WEST); 107 108 textField = new JTextField(); 109 panel.add(textField, BorderLayout.CENTER); 110 textField.setColumns(10); 111 112 JButton button = new JButton("\u53D1\u9001"); 113 button.addActionListener(new ActionListener() { 114 public void actionPerformed(ActionEvent e) { 115 // 获取发送信息 116 String sendContent = textField.getText().trim(); 117 // 获取发送对象 118 Info info = new Info(); 119 info.setFromUser(username); 120 info.setToUser((String) list.getSelectedValue()); 121 info.setContent(sendContent); 122 info.setInfoType(EnumInfoType.SEND_INFO); 123 System.out.println(info.getToUser()); 124 // 首先判断发送对象是否为空 125 if ("".equals(info.getToUser()) || info.getToUser() == null) { 126 JOptionPane.showMessageDialog(Clientframe, "请选择发送对象"); 127 return; 128 } else if (info.getToUser().equals(username)) { 129 JOptionPane.showMessageDialog(Clientframe, "不能对自己发送"); 130 return; 131 } else if (info.getContent().equals("") || info.getContent() == null) { 132 JOptionPane.showMessageDialog(Clientframe, "内容不能为空"); 133 return; 134 } else 135 us.send(info); 136 textField.setText(""); 137 } 138 }); 139 panel.add(button, BorderLayout.EAST); 140 } 141 142 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.Client; 2 3 import java.awt.Dimension; 4 5 @SuppressWarnings("unused") 6 public class LoginFrame { 7 8 private JFrame frame; 9 private JTextField id; 10 private JPasswordField pwd; 11 12 13 /** 14 * Launch the application. 15 */ 16 public static void main(String[] args) { 17 EventQueue.invokeLater(new Runnable() { 18 public void run() { 19 try { 20 LoginFrame window = new LoginFrame(); 21 window.frame.setVisible(true); 22 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 } 27 }); 28 } 29 30 /** 31 * Create the application. 32 */ 33 public LoginFrame() { 34 initialize(); 35 } 36 37 /** 38 * Initialize the contents of the frame. 39 */ 40 private void initialize() { 41 frame = new JFrame(); 42 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 43 frame.setVisible(true); 44 frame.setIconImage(Toolkit.getDefaultToolkit().getImage(LoginFrame.class.getResource("/com/gh/res/1.png"))); 45 Toolkit tk=Toolkit.getDefaultToolkit(); 46 Dimension d=tk.getScreenSize(); 47 int x=(int)(d.getWidth()-379)/2; 48 int y=(int)(d.getHeight()-171)/2; 49 frame.setResizable(false); 50 frame.setTitle("\u767B\u5F55"); 51 frame.setBounds(x, y, 379, 171); 52 frame.getContentPane().setLayout(new GridLayout(3, 1, 20, 5)); 53 54 JPanel panel = new JPanel(); 55 frame.getContentPane().add(panel); 56 FlowLayout fl_panel = new FlowLayout(FlowLayout.CENTER, 5, 10); 57 fl_panel.setAlignOnBaseline(true); 58 panel.setLayout(fl_panel); 59 60 JLabel label = new JLabel("\u7528\u6237\u5E10\u53F7\uFF1A"); 61 label.setFont(new Font("微软雅黑", Font.PLAIN, 15)); 62 panel.add(label); 63 64 id = new JTextField(); 65 panel.add(id); 66 id.setColumns(15); 67 68 69 JPanel panel_1 = new JPanel(); 70 FlowLayout flowLayout = (FlowLayout) panel_1.getLayout(); 71 flowLayout.setAlignOnBaseline(true); 72 flowLayout.setVgap(10); 73 frame.getContentPane().add(panel_1); 74 75 JLabel label_1 = new JLabel("\u7528\u6237\u5BC6\u7801\uFF1A"); 76 label_1.setFont(new Font("微软雅黑", Font.PLAIN, 15)); 77 panel_1.add(label_1); 78 79 pwd = new JPasswordField(); 80 pwd.setColumns(15); 81 panel_1.add(pwd); 82 83 JPanel panel_2 = new JPanel(); 84 FlowLayout flowLayout_1 = (FlowLayout) panel_2.getLayout(); 85 flowLayout_1.setVgap(6); 86 frame.getContentPane().add(panel_2); 87 88 JButton button = new JButton("\u767B\u5F55"); 89 button.addActionListener(new ActionListener() { 90 public void actionPerformed(ActionEvent arg0) { 91 String username=id.getText().trim(); 92 String password=new String(pwd.getPassword()); 93 if("".equals(username)||username==null||"".equals(password)||password==null){ 94 JOptionPane.showMessageDialog(frame, "用户名和密码不能为空"); 95 return; 96 } 97 new UserService().login(username, password,frame); 98 } 99 }); 100 button.setFont(new Font("微软雅黑", Font.PLAIN, 15)); 101 panel_2.add(button); 102 103 JButton button_1 = new JButton("\u9000\u51FA"); 104 button_1.addActionListener(new ActionListener() { 105 public void actionPerformed(ActionEvent e) { 106 int v=JOptionPane.showConfirmDialog(frame, "真的要退出吗", "退出", JOptionPane.YES_NO_OPTION); 107 if(v==JOptionPane.YES_OPTION)System.exit(0); 108 } 109 }); 110 button_1.setFont(new Font("微软雅黑", Font.PLAIN, 15)); 111 panel_2.add(button_1); 112 } 113 114 115 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.Client; 2 3 import java.io.IOException; 4 import java.io.ObjectInputStream; 5 import java.io.ObjectOutputStream; 6 import java.net.Socket; 7 8 import javax.swing.JFrame; 9 import javax.swing.JOptionPane; 10 11 import com.gh.model.Info; 12 import com.gh.model.LoginInfo; 13 14 public class UserService { 15 private LoginInfo logininfo = null; 16 private ClientFrame clientframe = null; 17 private boolean flag = true; 18 private Info getInfo = null; 19 public boolean isFlag() { 20 return flag; 21 } 22 public void setFlag(boolean flag) { 23 this.flag = flag; 24 } 25 private ObjectOutputStream bw; 26 private ObjectInputStream br; 27 public void login(final String username, final String password, final JFrame frame) { 28 //接收消息线程 29 Thread t=new Thread(new Runnable() { 30 @Override 31 public void run() { 32 try { 33 Socket sk = new Socket("192.168.1.102", 8000); 34 //这里被堵了好久,必须要先创建bw再创建br,因为scoket不许客户端和服务端同时创建输入流否者会堵塞Scoket通道 35 bw =new ObjectOutputStream(sk.getOutputStream()); 36 br = new ObjectInputStream(sk.getInputStream()); 37 logininfo = new LoginInfo(username, password); 38 bw.writeObject(logininfo);//向服务器发送登录信息 39 bw.flush(); 40 //从服务接收登录信息 41 LoginInfo objflag = (LoginInfo) br.readObject(); 42 if (objflag.isFlag()) {// 如果可以登录 43 // 创建聊天界面 44 clientframe = new ClientFrame(logininfo.getName(),UserService.this); 45 // 聊天界面显示欢迎信息 46 clientframe.updateText("服务器:欢迎登录!"); 47 frame.dispose(); 48 // 等待接收消息 49 while (isFlag()) { 50 getInfo = new Info(); 51 getInfo = (Info) br.readObject(); 52 switch (getInfo.getInfoType()) { 53 case SEND_INFO: 54 clientframe.updateText("用户:"+getInfo.getFromUser()+"说:"+getInfo.getContent()); 55 break; 56 case ADD_USER: 57 clientframe.AddUserToList(getInfo.getContent()); 58 break; 59 case EXIT: 60 clientframe.DelUser(getInfo.getFromUser()); 61 clientframe.updateText("服务器:用户"+getInfo.getFromUser()+"下线了"); 62 break; 63 default: 64 break; 65 } 66 67 } 68 } else { 69 JOptionPane.showMessageDialog(frame, "用户名或密码错误"); 70 } 71 } catch (Exception e) { 72 JOptionPane.showMessageDialog(frame, "服务器连接异常"); 73 //e.printStackTrace(); 74 } 75 } 76 }); 77 t.start(); 78 } 79 //发送消息线程 80 public void send(Info info){ 81 try { 82 bw.writeObject(info); 83 bw.flush(); 84 } catch (IOException e) { 85 e.printStackTrace(); 86 } 87 } 88 }
服务端代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.Sever; 2 3 import java.awt.EventQueue; 4 5 public class ServerFrame { 6 7 private JFrame Severframe; 8 private JTextField textField; 9 private JTextArea textArea = null; 10 private DefaultListModel<String> model=null; 11 private JList<String> list=null; 12 /** 13 * Launch the application. 14 */ 15 public static void main(String[] args) { 16 EventQueue.invokeLater(new Runnable() { 17 public void run() { 18 try { 19 ServerFrame window = new ServerFrame(); 20 window.Severframe.setVisible(true); 21 // 设置UI风格为系统默认的风格 22 //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 } 27 }); 28 29 } 30 //初始化用户列表 31 public void InitUserList(){ 32 model=new DefaultListModel<String>(); 33 model.addElement("所有人"); 34 list.setModel(model); 35 } 36 //添加用户到在线列表 37 public void AddUserToList(String username){ 38 model.addElement(username); 39 list.setModel(model); 40 } 41 public void updateText(String text) { 42 StringBuffer sb = new StringBuffer(); 43 sb.append(textArea.getText()).append("\n").append(DateUtil.getTime()+"--") 44 .append(text); 45 textArea.setText(sb.toString()); 46 } 47 public void DelUser(String username){ 48 model.removeElement(username); 49 } 50 /** 51 * Create the application. 52 */ 53 public ServerFrame() { 54 initialize(); 55 // 启动服务 56 startSever(); 57 //初始化用户列表 58 InitUserList(); 59 } 60 61 private void startSever() { 62 textArea.setText("服务器已经启动,正在监听8000端口..."); 63 new Thread(new Runnable() { 64 @Override 65 public void run() { 66 new ServerService(ServerFrame.this).startSever(); 67 } 68 }).start(); 69 } 70 71 /** 72 * Initialize the contents of the frame. 73 */ 74 private void initialize() { 75 Severframe = new JFrame(); 76 Severframe 77 .setIconImage(Toolkit.getDefaultToolkit().getImage(ServerFrame.class.getResource("/com/gh/res/1.png"))); 78 Severframe.setTitle("\u804A\u804A-\u670D\u52A1\u7AEF"); 79 Toolkit tk = Toolkit.getDefaultToolkit(); 80 Dimension d = tk.getScreenSize(); 81 int x = (int) (d.getWidth() - 534) / 2; 82 int y = (int) (d.getHeight() - 383) / 2; 83 Severframe.setBounds(x, y, 534, 383); 84 Severframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 85 Severframe.getContentPane().setLayout(new BorderLayout(0, 0)); 86 87 JLabel label = new JLabel("\u670D\u52A1\u5668\u7AEF"); 88 Severframe.getContentPane().add(label, BorderLayout.NORTH); 89 90 JPanel jpanel = new JPanel(); 91 Severframe.getContentPane().add(jpanel, BorderLayout.EAST); 92 jpanel.setLayout(new BorderLayout(0, 0)); 93 94 JLabel lblNewLabel = new JLabel("--\u5728\u7EBF\u7528\u6237\u5217\u8868--"); 95 jpanel.add(lblNewLabel, BorderLayout.NORTH); 96 97 list = new JList<String>(); 98 jpanel.add(list, BorderLayout.CENTER); 99 100 JScrollPane scrollPane = new JScrollPane(); 101 Severframe.getContentPane().add(scrollPane, BorderLayout.CENTER); 102 103 textArea = new JTextArea(); 104 scrollPane.setViewportView(textArea); 105 106 JPanel panel = new JPanel(); 107 Severframe.getContentPane().add(panel, BorderLayout.SOUTH); 108 panel.setLayout(new BorderLayout(0, 0)); 109 110 JLabel lblNewLabel_1 = new JLabel("\u8BF7\u8F93\u5165\uFF1A"); 111 panel.add(lblNewLabel_1, BorderLayout.WEST); 112 113 textField = new JTextField(); 114 panel.add(textField, BorderLayout.CENTER); 115 textField.setColumns(10); 116 117 JButton button = new JButton("\u53D1\u9001"); 118 panel.add(button, BorderLayout.EAST); 119 } 120 121 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.Sever; 2 3 import java.io.IOException; 4 import java.io.ObjectInputStream; 5 import java.io.ObjectOutputStream; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 import java.util.Vector; 9 import java.util.concurrent.ExecutorService; 10 import java.util.concurrent.Executors; 11 12 import com.gh.model.Info; 13 import com.gh.model.LoginInfo; 14 import com.gh.util.EnumInfoType; 15 16 /** 17 * 服务器客户端监听 18 * 19 * @author ganhang 20 */ 21 public class ServerService { 22 private Info currInfo = null; 23 private ServerFrame serFrame; 24 private boolean flag; 25 private UserServerThread ust; 26 ExecutorService es = Executors.newScheduledThreadPool(1); 27 // 保存所有在线用户服务线程 28 private Vector<UserServerThread> userThreads; 29 30 public ServerService(ServerFrame serFrame) { 31 this.serFrame = serFrame; 32 } 33 34 public void startSever() { 35 flag = true; 36 try { 37 ServerSocket server = new ServerSocket(8000); 38 userThreads = new Vector<UserServerThread>(); 39 while (flag) { 40 Socket client = server.accept(); 41 System.out.println("Server:有用户连接"); 42 ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 43 ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream()); 44 LoginInfo logininfo = (LoginInfo) ois.readObject();// 获取用户帐号密码 45 System.out.println(logininfo); 46 // 登录验证 47 if (UserLogin.islogin(logininfo.getName(), logininfo.getPwd())) { 48 logininfo.setFlag(true); 49 oos.writeObject(logininfo);// 发送确认登录信息 50 oos.flush(); 51 // 保存当前用户信息 52 currInfo = new Info(); 53 currInfo.setFromUser(logininfo.getName()); 54 // 在服务器端显示用户登录信息 55 serFrame.updateText("用户" + logininfo.getName() + "--已上线!"); 56 // 创建用户服务线程 57 UserServerThread ust = new UserServerThread(currInfo, ois, oos);// 创建用户服务进程 58 userThreads.add(ust);// 添加到进程池 59 es.execute(ust);// 启动线程 60 } else 61 oos.writeObject(logininfo); 62 } 63 } catch (IOException | ClassNotFoundException e) { 64 e.printStackTrace(); 65 } 66 } 67 68 /** 69 * 用户服务线程 70 * 71 * @author ganhang 72 */ 73 class UserServerThread implements Runnable { 74 private Info info = null; 75 private ObjectOutputStream oos; 76 private ObjectInputStream ois; 77 78 public UserServerThread(Info info, ObjectInputStream ois, ObjectOutputStream oos) { 79 this.info = info; 80 this.oos = oos; 81 this.ois = ois; 82 } 83 84 @Override 85 public void run() { 86 // 更新服务器的在线用户列表 87 serFrame.AddUserToList(currInfo.getFromUser()); 88 // 先获得在线用户的列表 89 StringBuffer sb = new StringBuffer(); 90 for (UserServerThread u : userThreads) { 91 sb.append(u.info.getFromUser()).append(","); 92 } 93 System.out.println(sb.toString()); 94 // 首先向线程池里每个用户发送一个自己的在线用户列表 95 for (UserServerThread usts : userThreads) { 96 try { 97 currInfo.setInfoType(EnumInfoType.ADD_USER); 98 currInfo.setContent(sb.toString()); 99 usts.oos.writeObject(currInfo);// 注意要写那个线程的输出流 100 usts.oos.flush(); 101 } catch (IOException e) { 102 e.printStackTrace(); 103 } 104 } 105 // 等待接收用户信息 106 System.out.println(flag); 107 new Thread(new Wait(ois, oos)).start(); 108 109 } 110 111 public void send(Info sendinfo) throws IOException { 112 oos.writeObject(sendinfo); 113 oos.flush(); 114 } 115 } 116 117 // 接收信息 118 class Wait implements Runnable { 119 private ObjectOutputStream oos; 120 private ObjectInputStream ois; 121 private boolean wait = true; 122 123 public Wait(ObjectInputStream ois, ObjectOutputStream oos) { 124 this.oos = oos; 125 this.ois = ois; 126 } 127 128 @Override 129 public void run() { 130 while (wait) { 131 try { 132 // 接收信息必须重开线程因为会把当前线程阻塞 133 Info info = (Info) ois.readObject(); 134 System.out.println(Thread.currentThread().getName() + "收到一个send信息"); 135 System.out.println(info); 136 switch (info.getInfoType()) { 137 case SEND_INFO: 138 if (info.getToUser().equals("所有人")) { 139 for (UserServerThread user : userThreads) { 140 //这里容易发错人,注意每个用户都有一个服务线程,哪个用户要接收信息就由它的服务线程去发给它,其他用户的服务线程发不了。 141 if (!info.getFromUser().equals(user.info.getFromUser())) 142 user.send(info); 143 System.out.print(user.info.getFromUser() + "/"); 144 } 145 System.out.println("消息发送名单"); 146 } else { 147 for (UserServerThread user : userThreads) { 148 if (info.getToUser().equals(user.info.getFromUser())) { 149 user.send(info); 150 } 151 } 152 } 153 break; 154 case EXIT: 155 serFrame.DelUser(info.getFromUser()); 156 serFrame.updateText("用户" + info.getFromUser() + "下线了"); 157 for (UserServerThread user : userThreads) { 158 if (!info.getFromUser().equals(user.info.getFromUser())) { 159 user.send(info); 160 } 161 //这里ust保存的是最后登录的用户的线程因为一直在被覆盖, 162 //需要重新写如当前要删的 163 else ust = user; 164 System.out.print(user.info.getFromUser() + ","); 165 } 166 System.out.println("退出发送名单"); 167 // 删除服务器上的该在线用户 168 wait = false; 169 System.out.println("移除的用户:" + ust.info.getFromUser()); 170 //这里容易删错用户线程 171 userThreads.remove(ust); 172 break; 173 default: 174 break; 175 } 176 } catch (ClassNotFoundException | IOException e) { 177 e.printStackTrace(); 178 } 179 180 } 181 } 182 } 183 184 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.Sever; 2 /** 3 * 登录验证,以后加数据库在写 4 * @author ganhang 5 */ 6 public class UserLogin { 7 public static boolean islogin(String name ,String pwd){ 8 return true; 9 } 10 }
对象模型
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.model; 2 3 import java.io.Serializable; 4 5 import com.gh.util.EnumInfoType; 6 /** 7 * 消息对象 8 * @author ganhang 9 * 10 */ 11 public class Info implements Serializable{ 12 private String fromUser;//信息来自哪 13 private String toUser;//发给谁 14 private String content;//内容 15 private EnumInfoType infoType;//信息类型 16 public String getFromUser() { 17 return fromUser; 18 } 19 public void setFromUser(String fromUser) { 20 this.fromUser = fromUser; 21 } 22 public String getToUser() { 23 return toUser; 24 } 25 public void setToUser(String toUser) { 26 this.toUser = toUser; 27 } 28 29 public String getContent() { 30 return content; 31 } 32 public void setContent(String content) { 33 this.content = content; 34 } 35 public EnumInfoType getInfoType() { 36 return infoType; 37 } 38 public void setInfoType(EnumInfoType infoType) { 39 this.infoType = infoType; 40 } 41 public Info(String fromUser, String toUser, String sendTime, String content, EnumInfoType infoType) { 42 super(); 43 this.fromUser = fromUser; 44 this.toUser = toUser; 45 46 this.content = content; 47 this.infoType = infoType; 48 } 49 @Override 50 public String toString() { 51 return "Info [fromUser=" + fromUser + ", toUser=" + toUser + ", content=" + content 52 + ", infoType=" + infoType + "]"; 53 } 54 public Info() { 55 super(); 56 } 57 58 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.model; 2 3 import java.io.Serializable; 4 5 @SuppressWarnings("serial") 6 public class LoginInfo implements Serializable{ 7 private String name; 8 private String pwd; 9 private boolean flag=false; 10 public String getName() { 11 return name; 12 } 13 public void setName(String name) { 14 this.name = name; 15 } 16 public String getPwd() { 17 return pwd; 18 } 19 public void setPwd(String pwd) { 20 this.pwd = pwd; 21 } 22 public boolean isFlag() { 23 return flag; 24 } 25 public void setFlag(boolean flag) { 26 this.flag = flag; 27 } 28 public LoginInfo(String name, String pwd) { 29 super(); 30 this.name = name; 31 this.pwd = pwd; 32 } 33 public LoginInfo() { 34 super(); 35 } 36 @Override 37 public String toString() { 38 return "LoginInfo [name=" + name + ", pwd=" + pwd + ", flag=" + flag + "]"; 39 } 40 41 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.gh.model; 2 /** 3 * 用户模型 4 * @author ganhang 5 * 暂时没用,因为没涉及数据库和一些用户管理功能 6 */ 7 public class User { 8 private String name; 9 private String age; 10 private String pwd; 11 private String email; 12 public String getName() { 13 return name; 14 } 15 public void setName(String name) { 16 this.name = name; 17 } 18 public String getAge() { 19 return age; 20 } 21 public void setAge(String age) { 22 this.age = age; 23 } 24 public String getPwd() { 25 return pwd; 26 } 27 public void setPwd(String pwd) { 28 this.pwd = pwd; 29 } 30 public String getEmail() { 31 return email; 32 } 33 public void setEmail(String email) { 34 this.email = email; 35 } 36 @Override 37 public String toString() { 38 return "User [name=" + name + ", age=" + age + ", pwd=" + pwd + ", email=" + email + "]"; 39 } 40 public User(String name, String age, String pwd, String email) { 41 super(); 42 this.name = name; 43 this.age = age; 44 this.pwd = pwd; 45 this.email = email; 46 } 47 public User() { 48 super(); 49 } 50 public User(String name, String pwd) { 51 super(); 52 this.name = name; 53 this.pwd = pwd; 54 } 55 public User(String name) { 56 super(); 57 this.name = name; 58 } 59 60 }
整个代码:https://github.com/ganhang/My-ChatSystem