【问题标题】:Problem in setting date and time from java code从java代码设置日期和时间的问题
【发布时间】:2020-05-15 19:56:14
【问题描述】:

我正在尝试使用 Java 从远程系统设置 linux 系统的日期和时间。为了做到这一点,我创建了一个服务器来接受来自远程系统的时间:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Set_date_n_time {
    public static void main(String[] args) throws IOException, InterruptedException {
        // TODO Auto-generated method stub
        String date_time = new String();
        //@SuppressWarnings("resource")
        ServerSocket s1 = new ServerSocket(7105);
        System.out.println("server started");
        while (true) {
            Socket sckt = s1.accept();
            InputStream input = sckt.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
            date_time = reader.readLine(); 
            String command="sudo date -s "+"\""+date_time+"\"";
            Process p;
            try {
                p = Runtime.getRuntime().exec(command);
                p.waitFor();
                System.out.println ("date set");
                p.destroy();
            } catch (Exception e) {} 
        }
}
}

以及从中复制时间的远程系统:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Fix_my_Date {

    public static void main(String args[]) throws IOException {
        String addr_list=args[0];
        String[] hostList = readAddressList(addr_list);
        for(int i=0; i<hostList.length;i++) {
        setDate(hostList[i]);
        }
    }


    //@SuppressWarnings("resource")
    private static void setDate(String address) throws IOException {
        {
            Scanner sc = new Scanner(System.in);
            Socket s = null;
            String date =new String();
            String time = new String();
            try {
                s = new Socket(address, 7105);
                System.out.println("connection to "+address+" done");
                Process p, p1;
                try {
                    p = Runtime.getRuntime().exec("date +%Y%m%d");
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(p.getInputStream()));
                            date = br.readLine();
                            br.close();
                    p.waitFor();
                    p.destroy();
                    p1 = Runtime.getRuntime().exec("date +%H:%M:%S");
                    BufferedReader br1 = new BufferedReader(
                            new InputStreamReader(p1.getInputStream()));
                            time = br1.readLine();
                            br1.close();
                    p1.waitFor();
                    p1.destroy();
                    PrintStream pr = new PrintStream(s.getOutputStream());
                    pr.print(date+" "+time+"");
                    sc.close();
                    s.close();
                } catch (Exception e) {
                    System.out.println("Problem Setting date and time");
                } 


                //s.close();
            } catch (Exception e) {
                System.out.println("Couldn't connect to: "+address+"");
                sc.close();

                //s.close();
            }

        }
        return;
    }


    private static String[] readAddressList(String addr_list) throws IOException {
        FileReader fileReader = new FileReader(addr_list);

        BufferedReader bufferedReader = new BufferedReader(fileReader);
        List<String> lines = new ArrayList<String>();
        String line = null;

        while ((line = bufferedReader.readLine()) != null) 
        {
            lines.add(line);
        }

        bufferedReader.close();

        System.out.println("Loaded the host list");
        return lines.toArray(new String[lines.size()]);

    }

}

但是服务器代码没有设置时间。我的错在哪里?

【问题讨论】:

  • 不要吞下异常。一个空的 catch 块是不可以的,很容易隐藏你的问题的答案。
  • 什么是输出?它可能需要更多 System.out.println 来提供更多关于故障所在的想法。
  • 假设你在 Linux 上运行,你必须确保你可以在不输入密码的情况下执行 'sudo'。您没有指定服务器的启动方式,但对于普通帐户,sudo 将需要每个会话的密码。另外,请将执行的命令记录在服务器上,包括返回码。
  • 请问为什么不能使用NTP? (因为您的机器都是基于 linux 的)还有 NTP 客户端/服务器的 Java 实现。
  • 好的@se7en 我明白了。以防万一可以告诉/欺骗 NTP 服务器将其本地机器时间提供给其客户端。也许在这里没有什么可学的,但谁知道...serverfault.com/questions/806274/…。至少重新发明的东西会更少,因为准确性不是问题..

标签: java linux datetime serversocket


【解决方案1】:

我、你和许多其他人犯的错误也是(除了这里的其他有用答案)你没有阅读标准输出和标准错误,如果你的命令产生任何输出或错误,它会阻塞,因为没有它可以写入的缓冲区,您可以使用 strace 观察。

这可以通过额外的线程来修复,如下所述:https://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html

如果您使用 sudo,它可能会失败,具体取决于 sudo 设置,如果它不允许没有终端的 sudo (requiretty),请参阅以获取更多信息,例如:https://bugzilla.redhat.com/show_bug.cgi?id=1196451

【讨论】:

    【解决方案2】:

    您是否尝试在虚拟机上设置时间?如果是这种情况,它可能被设置为与覆盖您的 date -s 命令的主机同步。

    我无法让它(还)与 Runtime.exec() 一起工作,但它与 ProcessBuilder 完美配合。这里是:

    package set_date_n_time;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Set_date_n_time {
    
        public static void main(String[] args) throws IOException, InterruptedException {
            // TODO Auto-generated method stub
            String date_time = new String();
            //@SuppressWarnings("resource")
            ServerSocket s1 = new ServerSocket(7105);
            System.out.println("server started");
            while (true) {
                Socket sckt = s1.accept();
                InputStream input = sckt.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(input));
                date_time = reader.readLine();
    
                ProcessBuilder builder = new ProcessBuilder("date", "--set=" + date_time + "");
                final Process p = builder.start();
                p.waitFor();
                p.destroy();
            }
        }
    }
    

    显然你必须以root身份运行并确保你运行

    sudo systemctl stop systemd-timesyncd.service

    或类似的东西,以确保系统不会覆盖您的日期修复。

    【讨论】:

    • 不是虚拟机。它们是两台不同的 linux 机器!
    • ...您在客户端机器上运行了“systemctl stop systemd-timesyncd.service”?
    • 我没有运行这个(systemctl stop systemd-timesyncd.service)。设备未连接到互联网。所以,我没有考虑到这一点。让我试试看。
    • -s 有效,但前提是我这样做:String[] command = new String[]{"sudo", "date", "-s", date_time};
    • 始终使用单独的参数(如"sudo", "date", "-s", date_time),不要使用会破坏命令的Runtime.exec(String)
    【解决方案3】:

    如果您需要或更喜欢使用 Runtime.exec(),那么(不要问我为什么)只需更改:

    String command = "sudo date -s " + "\"" + date_time + "\"";
    

    String[] command = new String[]{"sudo", "date", "-s", date_time};
    

    在 Set_date_n_time 类中。

    【讨论】:

    • 不要使用Runtime.exec();使用ProcessBuilderRuntime.exec() 本身在现代 Java 中调用)。
    【解决方案4】:

    替换这个:

    } catch (Exception e) {} 
    

    有了这个:

    } catch (Exception e) {
      throw new IllegalStateException("Unexpected exception", e);
    } 
    

    作为 Ole V.V.说,您的代码中的空捕获块几乎肯定会丢弃您关心的真正失败。我猜sudo 正在拒绝呼叫或命令本身格式错误。异常会告诉你到底出了什么问题。

    如果您发现抛出了您确实想忽略的异常,请单独处理它们,但您几乎不应该catch (Exception e) 并丢弃异常。


    使用ProcessBuilder 代替Runtime.exec() 也是一个好主意。这是一个更强大、更灵活的 API,用于与子流程进行交互。特别是,从不使用Runtime.exec(String);虽然它适用于简单的命令,但它不是 shell,并且对于带有特殊字符(如引号或空格)的命令会以令人惊讶的方式失败。

    例如:

    p = new ProcessBuilder("sudo", "date", "-s", date_time).start();
    

    【讨论】:

    • 是的,后来我尝试使用exception e,但控件没有进入 catch 块。我也尝试过使用流程构建器,但没有运气。感谢Runtime.exec()的缺点
    猜你喜欢
    • 2014-06-15
    • 1970-01-01
    • 2016-03-22
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    相关资源
    最近更新 更多