java学习之即时通信项目实战
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java学习之即时通信项目实战相关的知识,希望对你有一定的参考价值。
项目总结:这次项目主要是根据视频来的,结果跟到一半感觉跟不上,慢慢自己有了自己的想法,决定自己先不看学习视频,自己先试着写。
总结写前面,算是写的第一个项目吧。项目中遇到几点问题,首先Scoket对象创建后,服务器端和客户端不能同时创建输入流,否者会引起堵塞。
然后,读入流应该重新创建个线程做等待写入服务,因为读入流会引起当前线程进入阻塞状态。
还有一个用户线程对应一个服务线程,不是多个用户线程对应一个服务线程。
对对象的操作应该由那个对象本身提供操作方法,比如操作UI界面的变化应该由界面本身提拱。
最后最重要的是写代码之前应该先画个流程图,写代码时参数乱传的,哪里需要就调参数过来。导致思路不清.
首先是需求分析:
本次项目是模拟及时通信中最基本的功能,类似QQ的应用.
项目分为:
(1)服务器端:
服务器端主要负责用户管理,消息转发功能
(2) 客户端:
客户端主要负责用户间的消息发送
详细设计
服务器端
1、登录服务器后,进行客户端的监听,如有客户端连接,启动用户服务线程,与客户端进行交互。
2、如客户端向所有人发送消息,服务器将向所有在线用户广播该消息。
3、如客户端向指定人发送消息,服务器将查找接收人用户线程后转发消息。
4、用户登录后,向所有人更新用户列表。
客户端:
1、用户登录功能。
2、登录后用户可以看到在线用户列表
3 、向指定人发送消息
4、向所有人发送消息
代码的实现
首先,先构建界面图形出来
上面左边是用户界面(服务器界面和用户界面一样),右边是登录界面
由于全用手写代码比较麻烦,我用了可视化UI插件;
画出来如下图
界面做出来了,然后构建对象模型,
这里主要需要信息对象,和用户对象;
信息对象又分,登录信息,发送信息和退出信息。这里我把登录信息单独用一个对象构建,因为保存了帐号密码,以后好增加登录验证功能。
然后就是逻辑功能的实现了。
这里我发现看似简单的功能,实现起来还是有点麻烦的。学会了一点就是要模块化。不然自己很容易搞迷糊。
直接上代码:
客户端代码
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 }
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 }
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 }
服务端代码
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 = 以上是关于java学习之即时通信项目实战的主要内容,如果未能解决你的问题,请参考以下文章