RMI分布式时钟系统的设计与源码
posted @ 2012-11-25 07:27 from [FreedomShe]
分布式课程的作业据说是百年不变——在我老板还是学生的时候就已经是这个题目了。网上一搜一大堆类似代码,喜欢自己写代码,自己去研究探索学习,作业不难,记录下来给某人做参考。1. Java界面开发环境: Eclipse + Swing
2. RMI简介
3. 代码结构
1. Java界面开发环境: Eclipse + Swing
Java GUI 工具包主要有AWT, SWT, Swing三种(技术出现先后顺序),用Swing开发界面,代码还是很简洁的。IDE一般选择NetBeans或者Eclipse+插件,如今WindowBuilder被Google收购以后免费了,直接集成在了Eclipse IDE for Java Developers内,下载即可进行Java界面可视化开发。
Eclipse IDE for Java Developers-Eclipse Juno (4.2) SR1 Packages,下载(只有Eclipse IDE for Java Developers包含WindowBuilder,Eclipse IDE for Java EE Developers等版本均不包含WindowBuilder组件)。
参考:NetBeans VS Eclipse 开发SWT Swing应用
例程开发时Java版本:1.7.0_09
2. RMI简介
RMI即远程调用,它的介绍太多了。通俗点讲,这个过程就是服务器端实例化一个类,然后客户端可以通过RMI绑定IP和端口来调用服务器端的这个实例的方法或变量,就如同这个实例是在客户端一样(实例是在服务器端运行)。客户端通过RMI与服务器端通信只需要知道:服务器端的IP和端口,客户端注册的实例的类的接口。以本地IP为例编程实现简单RMI的一般步骤为:
1). 服务器端实例化一个类,并将其绑定在一个端口上(建立远程对象注册表,注册实例名等);
2). 客户端实例化一个接口(服务器端被调用实例的那个接口,客户端需要预先获得接口定义),而符合该接口定义的实例则是通过RMI远程获得。
3). 客户端可以远程调用符合这个接口定义的实例了。
最简单的RMI例程参考:Java RMI之HelloWorld篇
3. 代码结构
本例程的代码结构很清晰,共5个类,IService, ServiceImlp, RmiServer, RmiClient, ServerFrame, ClientFrame。他们的关系如下图:
可以看到,客户端程序包括IService, RmiClient, ClientFrame三个类,RmiClient封装了RMI远程调用相关的操作;ClientFrame则封装是客户端界面,也是程序的入口;IService是服务器端提供RMI服务的实例的接口类(如同C/C++里面调用一个dll,需要预先知道dll提供导出函数的头文件)。服务器端包括IService, ServiceImlp, RmiServer, ServerFrame四个类,IService前面已讲;ServiceImpl是IService的实现类,服务器端提供服务的实例就是由这个类实例化得到的;RmiServer是服务器端提供RMI服务操作的封装;ServerFrame则封装服务器端的界面。
IService为服务器端提供服务类实例的接口文件,服务器端必须拥有该接口文件的一个实现类ServiceImlp,最后提供服务的实例就由ServiceImpl实例化;客户端也必须知道该接口文件,以便将RMI实例转换为IService实例使用。
package services; import java.rmi.Remote; import java.rmi.RemoteException; /** * 服务接口,客户端调用接口,服务端实现接口 * @author yuanboshe */ public interface IService extends Remote { String getDateStr() throws RemoteException; String getMessage() throws RemoteException; }
package server; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.text.SimpleDateFormat; import java.util.Date; import services.IService; /** * 服务接口的实现 * @author yuanboshe */ public class ServiceImpl extends UnicastRemoteObject implements IService { private static final long serialVersionUID = 9076175201315559196L; public String message = ""; protected ServiceImpl() throws RemoteException { super(); } @Override public String getDateStr() throws RemoteException { return new SimpleDateFormat("yy-MM-dd hh:mm:ss").format(new Date()); } @Override public String getMessage() throws RemoteException { return message; } }
RmiServer是服务器端提供RMI服务的类,这里提供了绑定和取消绑定两个方法(取消绑定后客户端任然可以访问已经申请过的实例,但是不能再申请新的实例)。
package server; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; /** * 服务端RMI封装װ * @author Yuanbo She */ public class RmiServer { public Registry registry; public ServiceImpl service; /** * 启动RMI服务,需要定义本地IP监听端口 * @param port �˿ں� * @return */ public String bindService(String port) { try { if (registry != null && registry.list().length > 0) { return "Service already have bound!"; } service = new ServiceImpl(); registry = LocateRegistry.createRegistry(Integer.parseInt(port)); registry.rebind("service", service); return "Binding sucess!"; } catch (Exception e) { e.printStackTrace(); return "Error:\n" + e.getMessage(); } } /** * 取消绑定,但是客户端还能继续使用已经绑定的服务端实例,只是不能绑定新的实例 * @return */ public String unbindService() { try { if (registry == null || registry.list().length == 0) { return "No service have bound!"; } registry.unbind("service"); UnicastRemoteObject.unexportObject(registry, true); return "Unbinding sucess!"; } catch (Exception e) { e.printStackTrace(); return "Error:\n" + e.getMessage(); } } }
RmiClient封装了客户端的RMI操作
package client; import java.rmi.Naming; import java.rmi.RemoteException; import services.IService; /** * 客户端RMI封装 * @author yuanboshe */ public class RmiClient { IService service; public String setService(String ip, String port) { try { if (service == null) { String url = String.format("rmi://%s:%s/service", ip, port); service = (IService) Naming.lookup(url); return "Getting service sucess!"; } return "Service already exist!"; } catch (Exception e) { e.printStackTrace(); return "Binding failed!\n" + e.getMessage(); } } public void initService() { service = null; } public String callGetDateStr() throws RemoteException { if (service == null) { return null; } return service.getDateStr(); } public String callGetMessage() throws RemoteException { if (service == null) { return null; } return service.getMessage(); } }
ServerFrame和ClientFrame分别为服务器端和客户端的界面类,均集成自JFrame,Swing界面编写大体结构类似,一个main函数作为程序入口点,构造函数初始化界面,自己添加一些案件响应函数即可。本例中两个界面类均加入了一个定时器,server端定时器用于刷新时间,client端定时器用于向server端发送调用消息。安装Eclipse IDE for Java Developers后,用WindowBuilder Editor打开继承自JFrame的java文件(Eclipse里右键.java文件,选择Open with WindowBuilder Editor)即可进行可视化界面设计。
package server; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JTextField; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import java.awt.Font; import java.awt.Color; import javax.swing.JTextPane; /** * 服务端界面 * @author yuanboshe */ public class ServerFrame extends JFrame { private static final long serialVersionUID = -4596135066244941170L; private JPanel contentPane; private JTextField txtIp; private JTextField txtPort; private JLabel labelTime; private JTextPane txtpnMessage; private final String TIME_FORMAT = "yy-MM-dd hh:mm:ss"; private RmiServer rmiServer; private JTextField txtIntelMessage; private Timer updateDateTimer; /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { ServerFrame frame = new ServerFrame(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame. */ public ServerFrame() { setResizable(false); setTitle("RMI\u670D\u52A1\u7AEF"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 363, 386); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(new BorderLayout(0, 0)); JPanel panel = new JPanel(); contentPane.add(panel); GridBagLayout gbl_panel = new GridBagLayout(); gbl_panel.columnWidths = new int[] { 100, 100, 100, 0 }; gbl_panel.rowHeights = new int[] { 30, 30, 30, 30, 0, 80, 30, 0 }; gbl_panel.columnWeights = new double[] { 1.0, 1.0, 1.0, Double.MIN_VALUE }; gbl_panel.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE }; panel.setLayout(gbl_panel); JLabel lblip = new JLabel("\u672C\u673AIP\u5730\u5740\uFF1A"); GridBagConstraints gbc_lblip = new GridBagConstraints(); gbc_lblip.insets = new Insets(0, 0, 5, 5); gbc_lblip.anchor = GridBagConstraints.EAST; gbc_lblip.gridx = 0; gbc_lblip.gridy = 0; panel.add(lblip, gbc_lblip); String ip; try { ip = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e1) { e1.printStackTrace(); ip = "localhost"; } txtIp = new JTextField(); txtIp.setEditable(false); txtIp.setText(ip); GridBagConstraints gbc_txtIp = new GridBagConstraints(); gbc_txtIp.gridwidth = 2; gbc_txtIp.insets = new Insets(0, 0, 5, 0); gbc_txtIp.fill = GridBagConstraints.HORIZONTAL; gbc_txtIp.gridx = 1; gbc_txtIp.gridy = 0; panel.add(txtIp, gbc_txtIp); txtIp.setColumns(20); JLabel label_port = new JLabel("\u8981\u7ED1\u5B9A\u7684\u7AEF\u53E3\u53F7\uFF1A"); GridBagConstraints gbc_label_port = new GridBagConstraints(); gbc_label_port.insets = new Insets(0, 0, 5, 5); gbc_label_port.anchor = GridBagConstraints.EAST; gbc_label_port.gridx = 0; gbc_label_port.gridy = 1; panel.add(label_port, gbc_label_port); txtPort = new JTextField(); txtPort.setText("1099"); GridBagConstraints gbc_txtPort = new GridBagConstraints(); gbc_txtPort.gridwidth = 2; gbc_txtPort.insets = new Insets(0, 0, 5, 0); gbc_txtPort.fill = GridBagConstraints.HORIZONTAL; gbc_txtPort.gridx = 1; gbc_txtPort.gridy = 1; panel.add(txtPort, gbc_txtPort); txtPort.setColumns(20); JButton btnReset = new JButton("\u91CD\u7F6E"); btnReset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { resetAction(e); } }); GridBagConstraints gbc_btnReset = new GridBagConstraints(); gbc_btnReset.insets = new Insets(0, 0, 5, 5); gbc_btnReset.gridx = 0; gbc_btnReset.gridy = 2; panel.add(btnReset, gbc_btnReset); JButton btnStart = new JButton("\u542F\u52A8\u670D\u52A1"); btnStart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { startServerAction(e); } }); GridBagConstraints gbc_btnStart = new GridBagConstraints(); gbc_btnStart.insets = new Insets(0, 0, 5, 5); gbc_btnStart.anchor = GridBagConstraints.NORTH; gbc_btnStart.gridx = 1; gbc_btnStart.gridy = 2; panel.add(btnStart, gbc_btnStart); JButton btnStop = new JButton("\u505C\u6B62\u670D\u52A1"); btnStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { stopServerAction(e); } }); GridBagConstraints gbc_btnStop = new GridBagConstraints(); gbc_btnStop.insets = new Insets(0, 0, 5, 0); gbc_btnStop.gridx = 2; gbc_btnStop.gridy = 2; panel.add(btnStop, gbc_btnStop); JLabel label = new JLabel("\u5411\u5BA2\u6237\u7AEF\u53D1\u9001\u4FE1\u606F\uFF1A"); GridBagConstraints gbc_label = new GridBagConstraints(); gbc_label.gridwidth = 3; gbc_label.anchor = GridBagConstraints.WEST; gbc_label.insets = new Insets(0, 0, 5, 5); gbc_label.gridx = 0; gbc_label.gridy = 3; panel.add(label, gbc_label); txtIntelMessage = new JTextField(); GridBagConstraints gbc_txtIntelMessage = new GridBagConstraints(); gbc_txtIntelMessage.gridwidth = 2; gbc_txtIntelMessage.insets = new Insets(0, 0, 5, 5); gbc_txtIntelMessage.fill = GridBagConstraints.HORIZONTAL; gbc_txtIntelMessage.gridx = 0; gbc_txtIntelMessage.gridy = 4; panel.add(txtIntelMessage, gbc_txtIntelMessage); txtIntelMessage.setColumns(10); JButton btnSendMessage = new JButton("\u53D1\u9001"); btnSendMessage.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rmiServer.service.message = txtIntelMessage.getText(); } }); GridBagConstraints gbc_btnSendMessage = new GridBagConstraints(); gbc_btnSendMessage.insets = new Insets(0, 0, 5, 0); gbc_btnSendMessage.gridx = 2; gbc_btnSendMessage.gridy = 4; panel.add(btnSendMessage, gbc_btnSendMessage); labelTime = new JLabel(TIME_FORMAT); labelTime.setForeground(new Color(0, 51, 255)); labelTime.setFont(new Font("Times New Roman", Font.BOLD, 30)); GridBagConstraints gbc_labelTime = new GridBagConstraints(); gbc_labelTime.fill = GridBagConstraints.VERTICAL; gbc_labelTime.insets = new Insets(0, 0, 5, 0); gbc_labelTime.gridwidth = 3; gbc_labelTime.gridx = 0; gbc_labelTime.gridy = 5; panel.add(labelTime, gbc_labelTime); txtpnMessage = new JTextPane(); txtpnMessage.setEditable(false); txtpnMessage.setText("Message..."); txtpnMessage.setFont(new Font("Times New Roman", Font.ITALIC, 14)); txtpnMessage.setForeground(new Color(255, 0, 0)); GridBagConstraints gbc_txtpnMessage = new GridBagConstraints(); gbc_txtpnMessage.gridwidth = 3; gbc_txtpnMessage.fill = GridBagConstraints.BOTH; gbc_txtpnMessage.gridx = 0; gbc_txtpnMessage.gridy = 6; panel.add(txtpnMessage, gbc_txtpnMessage); } /** * 设置定时器任务 * @author Yuanbo She */ protected class MyTimerTask extends TimerTask { @Override public void run() { String time = new SimpleDateFormat(TIME_FORMAT).format(Calendar.getInstance().getTime()); labelTime.setText(time); } } private void startTimer() { if (updateDateTimer == null) { updateDateTimer = new Timer(); } updateDateTimer.scheduleAtFixedRate(new MyTimerTask(), new Date(), 800); } private void stopTimer() { if (updateDateTimer != null) { updateDateTimer.cancel(); updateDateTimer = null; } } public void startServerAction(ActionEvent e) { if (rmiServer == null) { rmiServer = new RmiServer(); } txtpnMessage.setText(rmiServer.bindService(txtPort.getText())); startTimer(); } public void stopServerAction(ActionEvent e) { if (rmiServer == null) { txtpnMessage.setText("Bound nothing, please start service first!"); return; } txtpnMessage.setText(rmiServer.unbindService()); stopTimer(); } public void resetAction(ActionEvent e) { txtIp.setText("localhost"); txtPort.setText("1099"); txtpnMessage.setText("Message..."); txtIntelMessage.setText(""); rmiServer.service.message = ""; } }
package client; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JTextField; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.Font; import java.awt.Color; import java.rmi.RemoteException; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import javax.swing.JTextPane; /** * 客户端界面 * @author yuanboshe */ public class ClientFrame extends JFrame { private static final long serialVersionUID = 4640407835597561733L; private JPanel contentPane; private JTextField txtIp; private JTextField txtPort; private JLabel lblTime; private JTextPane txtPnMessage; private RmiClient rmiClient; private JTextField txtIntelMessage; private Timer timer; /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { ClientFrame frame = new ClientFrame(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame. */ public ClientFrame() { setResizable(false); setTitle("RMI\u5BA2\u6237\u7AEF"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 377, 399); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(new BorderLayout(0, 0)); JPanel panel = new JPanel(); contentPane.add(panel); GridBagLayout gbl_panel = new GridBagLayout(); gbl_panel.columnWidths = new int[] { 120, 120, 120, 0 }; gbl_panel.rowHeights = new int[] { 30, 30, 30, 30, 30, 80, 0, 0 }; gbl_panel.columnWeights = new double[] { 0.0, 1.0, 1.0, Double.MIN_VALUE }; gbl_panel.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE }; panel.setLayout(gbl_panel); JLabel lblip = new JLabel("\u670D\u52A1\u5668IP\uFF1A"); GridBagConstraints gbc_lblip = new GridBagConstraints(); gbc_lblip.insets = new Insets(0, 0, 5, 5); gbc_lblip.anchor = GridBagConstraints.EAST; gbc_lblip.gridx = 0; gbc_lblip.gridy = 0; panel.add(lblip, gbc_lblip); txtIp = new JTextField(); txtIp.setText("localhost"); GridBagConstraints gbc_txtIp = new GridBagConstraints(); gbc_txtIp.gridwidth = 2; gbc_txtIp.insets = new Insets(0, 0, 5, 0); gbc_txtIp.fill = GridBagConstraints.HORIZONTAL; gbc_txtIp.gridx = 1; gbc_txtIp.gridy = 0; panel.add(txtIp, gbc_txtIp); txtIp.setColumns(20); JLabel label_port = new JLabel("\u670D\u52A1\u5668\u7AEF\u53E3\u53F7\uFF1A"); GridBagConstraints gbc_label_port = new GridBagConstraints(); gbc_label_port.insets = new Insets(0, 0, 5, 5); gbc_label_port.anchor = GridBagConstraints.EAST; gbc_label_port.gridx = 0; gbc_label_port.gridy = 1; panel.add(label_port, gbc_label_port); txtPort = new JTextField(); txtPort.setText("1099"); GridBagConstraints gbc_txtPort = new GridBagConstraints(); gbc_txtPort.gridwidth = 2; gbc_txtPort.insets = new Insets(0, 0, 5, 0); gbc_txtPort.fill = GridBagConstraints.HORIZONTAL; gbc_txtPort.gridx = 1; gbc_txtPort.gridy = 1; panel.add(txtPort, gbc_txtPort); txtPort.setColumns(20); JButton btnStart = new JButton("\u8FDE\u63A5\u670D\u52A1\u5668\u5E76\u542F\u52A8"); btnStart.addActionListener(new BtnStartAction()); JButton btnReset = new JButton("\u91CD\u7F6E\u6D88\u606F"); btnReset.addActionListener(new BtnResetAction()); GridBagConstraints gbc_btnReset = new GridBagConstraints(); gbc_btnReset.insets = new Insets(0, 0, 5, 5); gbc_btnReset.gridx = 0; gbc_btnReset.gridy = 2; panel.add(btnReset, gbc_btnReset); GridBagConstraints gbc_btnStart = new GridBagConstraints(); gbc_btnStart.gridwidth = 2; gbc_btnStart.insets = new Insets(0, 0, 5, 0); gbc_btnStart.gridx = 1; gbc_btnStart.gridy = 2; panel.add(btnStart, gbc_btnStart); JLabel label = new JLabel("\u670D\u52A1\u5668\u4F20\u6765\u7684\u4FE1\u606F\uFF1A"); GridBagConstraints gbc_label = new GridBagConstraints(); gbc_label.anchor = GridBagConstraints.EAST; gbc_label.insets = new Insets(0, 0, 5, 5); gbc_label.gridx = 0; gbc_label.gridy = 3; panel.add(label, gbc_label); txtIntelMessage = new JTextField(); txtIntelMessage.setEditable(false); GridBagConstraints gbc_txtIntelMessage = new GridBagConstraints(); gbc_txtIntelMessage.gridwidth = 2; gbc_txtIntelMessage.insets = new Insets(0, 0, 5, 5); gbc_txtIntelMessage.fill = GridBagConstraints.HORIZONTAL; gbc_txtIntelMessage.gridx = 1; gbc_txtIntelMessage.gridy = 3; panel.add(txtIntelMessage, gbc_txtIntelMessage); txtIntelMessage.setColumns(10); JLabel label_1 = new JLabel("\u670D\u52A1\u5668\u540C\u6B65\u6765\u7684\u65F6\u95F4\uFF1A"); GridBagConstraints gbc_label_1 = new GridBagConstraints(); gbc_label_1.anchor = GridBagConstraints.WEST; gbc_label_1.gridwidth = 3; gbc_label_1.insets = new Insets(0, 0, 5, 5); gbc_label_1.gridx = 0; gbc_label_1.gridy = 4; panel.add(label_1, gbc_label_1); lblTime = new JLabel("yy-MM-dd hh:mm:ss"); lblTime.setForeground(new Color(0, 51, 255)); lblTime.setFont(new Font("Times New Roman", Font.BOLD, 30)); GridBagConstraints gbc_lblTime = new GridBagConstraints(); gbc_lblTime.insets = new Insets(0, 0, 5, 0); gbc_lblTime.gridwidth = 3; gbc_lblTime.gridx = 0; gbc_lblTime.gridy = 5; panel.add(lblTime, gbc_lblTime); txtPnMessage = new JTextPane(); txtPnMessage.setText("Message..."); txtPnMessage.setForeground(Color.RED); txtPnMessage.setFont(new Font("Times New Roman", Font.ITALIC, 14)); txtPnMessage.setEditable(false); GridBagConstraints gbc_txtPnMessage = new GridBagConstraints(); gbc_txtPnMessage.gridwidth = 3; gbc_txtPnMessage.fill = GridBagConstraints.BOTH; gbc_txtPnMessage.gridx = 0; gbc_txtPnMessage.gridy = 6; panel.add(txtPnMessage, gbc_txtPnMessage); rmiClient = new RmiClient(); } protected class BtnResetAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { lblTime.setText("yy-MM-dd hh:mm:ss"); txtIp.setText("localhost"); txtPort.setText("1099"); } } protected class BtnStartAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { txtPnMessage.setText(rmiClient.setService(txtIp.getText(), txtPort.getText())); // 增加定时器 if (timer == null) { timer = new Timer(); } timer.scheduleAtFixedRate(new MyTimerTask(), new Date(), 1000); } } // 定时器定时执行的任务 protected class MyTimerTask extends TimerTask { @Override public void run() { try { lblTime.setText(rmiClient.callGetDateStr()); txtIntelMessage.setText(rmiClient.callGetMessage()); } catch (RemoteException e) { e.printStackTrace(); timer.cancel(); timer = null; rmiClient.initService(); txtPnMessage.setText("Error:\n" + e.getMessage()); } } } }
以上是全部源码,按照services, client, server三包结构存放即可。