package com.casic.swing.ui; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.casic.swing.utils.*; import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.CharsetUtil; import okhttp3.Request; import javax.swing.*; import java.awt.*; import java.net.InetAddress; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author a203 */ public class TimeGuardNtp extends JFrame { private JPanel ntpPanel; private JLabel currentTimeLabel; private JPanel dotPanel; private JLabel stateView; private JLabel recentlyTimeLabel; private JLabel frequencyLabel; private JTextField hostTextField; private JComboBox<String> localHostBox; private JCheckBox autoCheckBox; private JButton updateTimeButton; private JLabel udpPortLabel; private JLabel udpPortTitle; private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build()); private ScheduledFuture future; private boolean hasNtp = false; private final String serverConfig; private boolean isTaskRunning = false; public static void main(String[] args) { new TimeGuardNtp(); } public TimeGuardNtp() { setContentPane(ntpPanel); setResizable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setSize(400, 300); //居中 setLocationRelativeTo(null); setVisible(true); stateView.setText("未同步"); recentlyTimeLabel.setText("无法确定最近同步时间"); setStateView(Color.GRAY); //检查环境 new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() { hasNtp = CommandUtil.checkEnv(); return hasNtp; } @Override protected void done() { if (!hasNtp) { JOptionPane.showMessageDialog(ntpPanel, "未当前设备发现可用的NTP配置", "Runtime Error", JOptionPane.ERROR_MESSAGE); //环境不对,关闭窗体 dispose(); } super.done(); } }.execute(); // 获取后台地址的配置 serverConfig = ConfigHelper.getConfigData(); // 每秒更新当前时间 updateCurrentTimePerSecond(); // 获取同步时间参数 getParamTimeSync(); // 获取监听的udp端口 getParamCmdUdpPort(); // 获取默认的ntp服务器地址 getNtpServerAddress(); //初始化JComboBox List<InetAddress> addressList = HttpRequestHelper.localHost(); for (InetAddress address : addressList) { localHostBox.addItem(address.getHostAddress()); } // 绑定自动同步复选框事件 autoCheckBox.addActionListener(e -> { JCheckBox checkBox = (JCheckBox) e.getSource(); if (checkBox.isSelected()) { updateTimeButton.setEnabled(false); hostTextField.setEnabled(false); String host = hostTextField.getText().trim(); if (host.isEmpty()) { autoCheckBox.setSelected(false); updateTimeButton.setEnabled(true); hostTextField.setEnabled(true); JOptionPane.showMessageDialog(ntpPanel, "授时中心服务器输入错误,请检查", "Runtime Error", JOptionPane.ERROR_MESSAGE); return; } // 立即执行一次任务 future = executorService.schedule(this::updateView, 1, TimeUnit.SECONDS); } else { if (null != future) { future.cancel(true); } updateTimeButton.setEnabled(true); hostTextField.setEnabled(true); } }); //按钮点击事件 updateTimeButton.addActionListener(e -> updateView()); } private void updateView() { String host = hostTextField.getText().trim(); if (host.isEmpty()) { autoCheckBox.setSelected(false); updateTimeButton.setEnabled(true); hostTextField.setEnabled(true); JOptionPane.showMessageDialog(ntpPanel, "授时中心服务器输入错误,请检查", "Runtime Error", JOptionPane.ERROR_MESSAGE); return; } if (null != future) { future.cancel(false); } // 重新获取同步周期 getParamTimeSync(); // String result = CommandUtil.simulateNtpResult(); String result = CommandUtil.ntpDate(host); LogToFile.save(result); // 重新启动定时任务 if (autoCheckBox.isSelected() == true) { future = executorService.schedule(this::updateView, Integer.parseInt(frequencyLabel.getText()) * 60 - 5, TimeUnit.SECONDS); } // 向后台接口提交ntp同步校时的结果 JSONObject object = new JSONObject(); if ("".equals(result)) { setStateView(Color.RED); stateView.setText("同步失败"); recentlyTimeLabel.setText(""); object.put("deltaTime", ""); object.put("status", 0); } else { String systemTime = TimeOrDateUtil.timestampToTime(); String resultString = result.split(":")[3]; boolean isSuccess = !resultString.contains("no server suitable"); if (isSuccess) { setStateView(Color.GREEN); stateView.setText("同步成功"); recentlyTimeLabel.setText(systemTime); String[] dataSplit = resultString.split(" "); object.put("deltaTime", dataSplit[6]); object.put("status", 1); } else { setStateView(Color.RED); stateView.setText("同步失败"); recentlyTimeLabel.setText(""); object.put("deltaTime", ""); object.put("status", 0); } //POST提交 try { object.put("deviceIp", localHostBox.getSelectedItem()); object.put("ntpResult", result); object.put("createTime", systemTime); } catch (JSONException e) { e.printStackTrace(); } Request request = new Request.Builder() .url(serverConfig + Constant.SYNCHRONIZE_URL) .post(HttpRequestHelper.createRequestBody(object.toJSONString())) .build(); HttpRequestHelper.doHttpRequest(request, new IHttpCallback() { @Override public void onSuccess(String s) { System.out.println("保存同步结果成功:" + object.get("deltaTime")); } @Override public void onFailure(Exception e) { System.out.println("保存同步结果失败:" + e.getMessage()); } }); } } private void setStateView(Color color) { dotPanel.setPreferredSize(new Dimension(15, 15)); dotPanel.setBackground(color); } private void getParamTimeSync() { Request request = new Request.Builder().url(serverConfig + Constant.FREQUENCY_URL).build(); HttpRequestHelper.doHttpRequest(request, new IHttpCallback() { @Override public void onSuccess(String s) { frequencyLabel.setText(StringHelper.parseJson(s)); } @Override public void onFailure(Exception e) { } }); } private void getParamCmdUdpPort() { Request request = new Request.Builder().url(serverConfig + Constant.CMD_UDP_PORT).build(); HttpRequestHelper.doHttpRequest(request, new IHttpCallback() { @Override public void onSuccess(String s) { udpPortLabel.setText(StringHelper.parseJson(s)); startCmdUdpServer(); } @Override public void onFailure(Exception e) { } }); } private void getNtpServerAddress() { Request request = new Request.Builder().url(serverConfig + Constant.NTP_SERV_ADDR).build(); HttpRequestHelper.doHttpRequest(request, new IHttpCallback() { @Override public void onSuccess(String s) { System.out.println(StringHelper.parseJson(s)); hostTextField.setText(StringHelper.parseJson(s)); } @Override public void onFailure(Exception e) { hostTextField.setText(""); } }); } private void updateCurrentTimePerSecond() { // 当前时间字体颜色为蓝色 currentTimeLabel.setForeground(Color.BLUE); // 每秒更新 new Timer(1000, e -> { String systemTime = TimeOrDateUtil.timestampToTime(); currentTimeLabel.setText(systemTime); }).start(); } private void startCmdUdpServer() { EventLoopGroup workerGroup = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap();// b.group(workerGroup).channel(NioDatagramChannel.class)//设置UDP通道 .handler(new SimpleChannelInboundHandler() { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception { try { DatagramPacket datagramPacket = (DatagramPacket) o; String msg = datagramPacket.content().toString(CharsetUtil.UTF_8); if (msg.trim().toUpperCase().equals("SYNC")) { updateView(); } } catch (Exception e) { throw e; } } })//初始化处理器 .option(ChannelOption.SO_BROADCAST, true);// 支持广播 // 绑定端口,开始接收进来的连接 ChannelFuture f = b.bind(Integer.parseInt(udpPortLabel.getText())).sync(); udpPortTitle.setText("监听端口[已监听]:"); //让线程进入wait状态,也就是main线程暂时不会执行到finally里面,nettyserver也持续运行,如果监听到关闭事件,可以优雅的关闭通道和nettyserver f.channel().closeFuture().sync(); } catch (Exception e) { // TODO: handle exception System.out.println("Exception:" + e.getMessage()); workerGroup.shutdownGracefully(); } } }