Связь SSH Сервер-Клиент, поток чтения-записи из сокетов - PullRequest
0 голосов
/ 28 марта 2019

По сути, мне нужно сделать что-то вроде чата. Я настроил библиотеку JSch и, используя их примеры, смог успешно открыть SSH (-R) туннель между клиентом (Win10-local) и сервером (Rpi-remote). Я могу отправлять команды в шелл в rpi, передавать файлы, создавать новые файлы, записывать в них и т. Д. Но мне нужно иметь возможность «ловить» и читать данные на стороне сервера прямо из сокета, скажем, некоторая отправленная строка со стороны клиента. Затем я должен изменить эту строку на стороне сервера и отправить ее обратно клиенту, где она будет распечатана.

Я попытался создать сокет (сервер) и связать его с портом, по которому идет связь SSH, но я получаю ошибку, что сокет не может быть привязан к этому порту, потому что он уже используется. Если я сначала свяжу сокет, то туннель SSH не пройдет.

Я также попробовал пример демона в примерах jsch: http://www.jcraft.com/jsch/examples/Daemon.java.html но безуспешно.

Сейчас я пытаюсь использовать этот код на стороне сервера:

public class ServerSide {
    private final static Random random = new Random();
    private final static String[] ADVICE_LIST = {
        "Take smaller bites", 
        "Go for the tight jeans. No they do NOT make you look fat.", 
        "One word: inappropriate", 
    };

    private void go() throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress("localhost",12005));

        while(!serverSocket.isClosed()) {
            Socket socket = serverSocket.accept();

            PrintWriter writer = new PrintWriter(socket.getOutputStream());
            System.out.println(socket.getOutputStream());
            String advice = getAdvice();
            System.out.println("Sending advice: " + advice);
            writer.write(advice);
            writer.close();
            System.out.println("Advice sent!");
            socket.close();
        }
    }

    private static String getAdvice() {
        return ADVICE_LIST[random.nextInt() % ADVICE_LIST.length];
    }

    public static void main(String[] args) throws IOException {

        ServerSide server = new ServerSide();
        server.go();
    }
  }
}

Сервер прослушивает порт 12005 и, если он обнаруживает какого-либо клиента, он просто отправляет ему случайное сообщение. Когда я попробовал это вчера, с прослушиванием сервера, туннель не мог быть установлен, но когда я попробовал его, туннель был установлен, но сервер вообще ничего не обнаружил. Код на клиенте:

public class PortForwardingR{
  public static void main(String[] arg){

    int rport;
    String lhost;
    int lport;

    try{
      JSch jsch=new JSch();

      String host=null;
      if(arg.length>0){
        host=arg[0];
      }
      else{
        host=JOptionPane.showInputDialog("Enter username@hostname",
                                         System.getProperty("user.name")+
                                         "@localhost"); 
      }
      String user=host.substring(0, host.indexOf('@'));
      host=host.substring(host.indexOf('@')+1);

      Session session=jsch.getSession(user, host, 22);

      String foo=JOptionPane.showInputDialog("Enter -R port:host:hostport", 
                         "port:host:hostport");
      rport=Integer.parseInt(foo.substring(0, foo.indexOf(':')));
      foo=foo.substring(foo.indexOf(':')+1);
      lhost=foo.substring(0, foo.indexOf(':'));
      lport=Integer.parseInt(foo.substring(foo.indexOf(':')+1));

      // username and password will be given via UserInfo interface.
      UserInfo ui=new MyUserInfo();
      session.setUserInfo(ui);

      session.connect();

      // Channel channel=session.openChannel("shell");
      // channel.connect();

      session.setPortForwardingR(rport, lhost, lport);

      System.out.println(host+":"+rport+" -> "+lhost+":"+lport);
    }
    catch(Exception e){
      System.out.println(e);
    }
  }

  public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{
    public String getPassword(){ return passwd; }
    public boolean promptYesNo(String str){
      Object[] options={ "yes", "no" };
      int foo=JOptionPane.showOptionDialog(null, 
             str,
             "Warning", 
             JOptionPane.DEFAULT_OPTION, 
             JOptionPane.WARNING_MESSAGE,
             null, options, options[0]);
       return foo==0;
    }

    String passwd;
    JTextField passwordField=(JTextField)new JPasswordField(20);

    public String getPassphrase(){ return null; }
    public boolean promptPassphrase(String message){ return true; }
    public boolean promptPassword(String message){
      Object[] ob={passwordField}; 
      int result=
      JOptionPane.showConfirmDialog(null, ob, message,
                    JOptionPane.OK_CANCEL_OPTION);
      if(result==JOptionPane.OK_OPTION){
    passwd=passwordField.getText();
    return true;
      }
      else{ return false; }
    }
    public void showMessage(String message){
      JOptionPane.showMessageDialog(null, message);
    }
    final GridBagConstraints gbc = 
      new GridBagConstraints(0,0,1,1,1,1,
                             GridBagConstraints.NORTHWEST,
                             GridBagConstraints.NONE,
                             new Insets(0,0,0,0),0,0);
    private Container panel;
    public String[] promptKeyboardInteractive(String destination,
                                              String name,
                                              String instruction,
                                              String[] prompt,
                                              boolean[] echo){
      panel = new JPanel();
      panel.setLayout(new GridBagLayout());

      gbc.weightx = 1.0;
      gbc.gridwidth = GridBagConstraints.REMAINDER;
      gbc.gridx = 0;
      panel.add(new JLabel(instruction), gbc);
      gbc.gridy++;

      gbc.gridwidth = GridBagConstraints.RELATIVE;

      JTextField[] texts=new JTextField[prompt.length];
      for(int i=0; i<prompt.length; i++){
        gbc.fill = GridBagConstraints.NONE;
        gbc.gridx = 0;
        gbc.weightx = 1;
        panel.add(new JLabel(prompt[i]),gbc);

        gbc.gridx = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weighty = 1;
        if(echo[i]){
          texts[i]=new JTextField(20);
        }
        else{
          texts[i]=new JPasswordField(20);
        }
        panel.add(texts[i], gbc);
        gbc.gridy++;
      }

      if(JOptionPane.showConfirmDialog(null, panel, 
                                       destination+": "+name,
                                       JOptionPane.OK_CANCEL_OPTION,
                                       JOptionPane.QUESTION_MESSAGE)
         ==JOptionPane.OK_OPTION){
        String[] response=new String[prompt.length];
        for(int i=0; i<prompt.length; i++){
          response[i]=texts[i].getText();
        }
    return response;
      }
      else{
        return null;  // cancel
      }
    }
  }
}

Когда мне предлагают «Введите имя пользователя @ имя хоста», я ввожу RPI's_username @ It's_publicIP, затем в «Введите -R порт: хост: хостпорт» я ввожу: 12005: localhost: 12005. Я делаю это правильно? Соединение успешно установлено, поэтому я считаю, что все в порядке.

Кроме того, я нашел этот код здесь (попробовал и все заработало), что очень близко к тому, что я хочу, но работает с оболочкой, я хочу напрямую общаться с моим кодом на сервере сторона.

public class TestShell {

    public static void main(String[] arg) {

        try {

            JSch jsch = new JSch();
            String host = null;

            final Session session = jsch.getSession("user", "remotecomputer", 22);
            session.setPassword("fluffybunnyslippers");

            session.setConfig("StrictHostKeyChecking", "no");
            session.connect(30000);   // making a connection with timeout.

            final Channel channel = session.openChannel("shell");

            PipedInputStream pis = new PipedInputStream();
            final PipedOutputStream pos = new PipedOutputStream(pis);

            channel.setInputStream(pis);
            channel.setOutputStream(new OutputStream() {

                private int cmdIndx = 0;
                private String[] cmds = {
                    "ls\n",
                    "cd ..\n",
                    "ls\n",
                    "exit\n"
                };

                private String line = "";

                @Override
                public void write(int b) throws IOException {
                    char c = (char) b;
                    if (c == '\n') {
                        logout(line);
                        System.out.print(line);
                        line = "";
                    } else {
                        line += c;
                        logout(line);
                        if (line.endsWith("$ ")) {
                            String cmd = cmds[cmdIndx];
                            cmdIndx++;
                            pos.write(cmd.getBytes());
                        }
                    }
                }

                public void logout(String line) {
                    if (line.startsWith("logout")) {
                        System.out.println("...logout...");
                        channel.disconnect();
                        session.disconnect();
                        System.exit(0);
                    }
                }
            });

            channel.connect(3 * 1000);

        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

Rpi (сервер) находится на маршрутизаторе с публичным IP. Маршрутизатор установил переадресацию портов на порт 12005, который отправляет данные на локальный IP-адрес (и на порт 12005), где находится мой RPI. Порт маршрутизатора также имеет открытый порт 12005, вероятно, не обязательный, и я закрою его, если добьюсь того, что пытаюсь. Клиент просто на Wi-Fi с мобильной точки доступа.

Может кто-нибудь дать мне несколько советов о том, как использовать существующий работающий туннель SSH для чтения / записи сообщений непосредственно из сокетов? Я очень новичок в общении через интернет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...