2017-02-14 3 views
0

Ich arbeite auf einem einfachen FTP-Server, und der Client muss mehrere Nachrichten an den Server senden, und für jede Nachricht der Server zurück an den Client eine Antwort senden. Wenn der Client eine Nachricht sendet, funktioniert es einwandfrei und der Server antwortet ohne Probleme, zum Beispiel, wenn der Client "USER username" sendet, sendet der Server dem Client "password needed" zurück.mehrere Nachrichten über den gleichen Socket funktioniert nicht

Aber wenn der Client eine andere Nachricht sendet "PASS-Passwort" (mit der gleichen Buchse) funktioniert es nicht! NUR der erste Austausch funktioniert (für den Benutzernamen), wenn die erste Nachricht gesendet wird, der Server anwser ohne Probleme, aber es blockiert, wenn es die zweite Nachricht (für das Passwort) senden möchte.

bitte kann mir jeder helfen? Danke !!

hier ist mein Code:

@Test 
public void testProcessPASS() throws IOException{ 

    Socket socket = new Socket(server.getAddress(), server.getcmdPort()); 

    this.ClientReceiveMessage(socket); // to flush 
    String cmd = "USER user_test\r\n"; 
    this.ClientSendMessage(socket, cmd); 
    String anwser = this.ClientReceiveMessage(socket); 
    assertEquals("Response error.", Constants.MSG_331.replace("\r\n", ""), anwser); 

    //PROBLEME STARTS HERE :/ 

    String cmd2 = "PASS pass_test\r\n"; 
    this.ClientSendMessage(socket, cmd2); 
    String anwser2 = this.ClientReceiveMessage(socket); 
    assertEquals(Constants.MSG_230.replace("\r\n", ""), anwser2); 
    socket.close(); 

} 


public void ClientSendMessage(Socket skt, String msg) throws IOException{ 

    PrintWriter messageClient = new PrintWriter(new OutputStreamWriter(skt.getOutputStream()),true); 
    messageClient.println(msg); 
    messageClient.flush(); 

} 

public String ClientReceiveMessage(Socket skt) throws IOException{ 
    BufferedReader br = new BufferedReader(new InputStreamReader(skt.getInputStream())); 
    String res = br.readLine() ; 
    return res; 
} 

dies ist der Server-Code:

public class Server implements Runnable { 

private ServerSocket cmdserverSocket; 
private ServerSocket dataServerSocket; 

private boolean running; 

public Server() throws IOException { 
    this.cmdserverSocket = new ServerSocket(1024); 
    this.dataServerSocket = new ServerSocket(1025); 
    this.running = false; 
} 

public boolean isRunning() { 
    return this.running; 
} 

public InetAddress getAddress() { 
    return this.cmdserverSocket.getInetAddress(); 
} 

public int getcmdPort() { 
    return this.cmdserverSocket.getLocalPort(); 
} 

public int getDataPort() { 
    return this.dataServerSocket.getLocalPort(); 
} 

public void run() { 
    // TODO Auto-generated method stub 
    this.running = true; 
    System.out.println("server started on port : " + this.getcmdPort()); 

    while (this.running) { 
     try { 
      Socket socket = this.cmdserverSocket.accept(); 
      new Thread(new FtpRequest(socket, this.dataServerSocket)) 
        .start(); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      System.out.println("server error : " + e.getMessage()); 
      this.running = false; 
     } 
    } 
} 

} 

und das ist die Klasse, die Client-Anforderungen verarbeitet und sendet Nachrichten an Client und läuft auf einem neuen Thread :

public class FtpRequest implements Runnable { 

private Socket cmdSocket; 
private Socket dataSocket; 
private BufferedReader cmdBufferedReader; 
private DataOutputStream cmdDataOutputStream; 
private ServerSocket dataServerSocket; 
private boolean anonymous; 
private boolean connected; 
private String username; 
private boolean processRunning; 
private String directory; 

public FtpRequest(Socket cmds, ServerSocket dts) throws IOException { 

    this.cmdSocket = cmds; 
    this.dataServerSocket = dts; 
    this.cmdBufferedReader = new BufferedReader(new InputStreamReader(
      this.cmdSocket.getInputStream())); 
    this.cmdDataOutputStream = new DataOutputStream(
      this.cmdSocket.getOutputStream()); 
    this.anonymous = true; 
    this.connected = false; 
    this.username = Constants.ANONYMOUS_USER; 
    this.processRunning = true; 
    this.directory = "/home"; 

} 

/** 
* send a message on the socket of commands 
* 
* @param msg 
*   the msg to send on the socket of commands 
* @throws IOException 
*/ 
public void sendMessage(String msg) throws IOException { 
    System.out.println("FtpRequest sendMessage : " + msg); 
    PrintWriter messageClient = new PrintWriter(new OutputStreamWriter(
      this.cmdDataOutputStream), true); 
    messageClient.println(msg); 
    messageClient.flush(); 

    /* 
    * this.cmdDataOutputStream.writeBytes(msg); 
    * this.cmdDataOutputStream.flush(); this.cmdSocket.close(); 
    */ 
} 

public void run() { 
    // TODO Auto-generated method stub 
    System.out.println("FtpRequest running ..."); 
    try { 
     this.sendMessage(Constants.MSG_220); // service ready for new user 
     this.handleRequest(); 

    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } // service ready for new user 

} 

/** 
* this method handle the request readen from cmd socket and run the 
* required method 
* 
* @throws IOException 
*/ 
private void handleRequest() throws IOException { 

    String rqst = this.cmdBufferedReader.readLine(); 

    Request request = new Request(rqst); 
    System.out.println("FtpRequest handleRequest" + rqst); 

    switch (request.getType()) { 
    case USER: 
     this.processUSER(request); 
     break; 

    case PASS: 
     this.processPASS(request); 
     break; 

    default: 
     this.sendMessage(Constants.MSG_502); // Command not implemented.\r\n 
     break; 

    } 

    /* 
    * if (this.processRunning = true) this.handleRequest(); 
    * 
    * else { this.cmdSocket.close(); System.out.println("socket closed "); 
    * } 
    */ 

} 

private void processUSER(Request rqst) throws IOException { 

    System.out.println("FtpRequest processUSER"); 
    if (rqst.getArgument().equals(Constants.ANONYMOUS_USER)) { 
     this.sendMessage(Constants.MSG_230); // user loged in 
     this.connected = true; 
     this.anonymous = true; 
     this.username = Constants.ANONYMOUS_USER; 
    } else if (rqst.getArgument().equals(Constants.USER_TEST)) { 
     this.sendMessage(Constants.MSG_331); // User name okay, need 
               // password.\r\n 
     this.username = Constants.USER_TEST; 
    } else 
     this.sendMessage(Constants.MSG_332); 
} 

private void processPASS(Request rqst) throws IOException { 
    System.out.println("FtpRequest processPASS"); 
    if (rqst.getArgument().equals(Constants.USER_TEST) 
      && rqst.getArgument().equals(Constants.PASS_TEST)) { 
     this.sendMessage(Constants.MSG_230); 
     this.connected = true; 
     this.anonymous = false; 
    } else 
     this.sendMessage(Constants.MSG_332); // au cas seulement le mot de 
               // passe est fourni 
} 


} 
+0

@Remy Lebeau danke :) was soll ich mehrzeiligen Antworten zu tun haben? –

Antwort

2

Es gibt einige Probleme mit Ihrem Code.

ClientSendMessage() verwendet PrintWriter.println(), die einen Zeilenumbruch ausgibt. Aber Ihre Eingabezeichenfolgen bereits haben Zeilenumbrüche auf sie, so dass die println() zusätzliche Zeilenumbrüche sendet. Außerdem ist der Zeilenumbruch println() Ausgaben plattformabhängig, während FTP CRLF spezifisch verwendet. Sie sollten also println() überhaupt nicht verwenden.

ClientReceiveMessage() berücksichtigt keine Mehrfachzeilenantworten. Per RFC 959 „ANTWORTET FTP“ Abschnitt 4.2:

 
A reply is defined to contain the 3-digit code, followed by Space 
<SP>, followed by one line of text (where some maximum line length 
has been specified), and terminated by the Telnet end-of-line 
code. There will be cases however, where the text is longer than 
a single line. In these cases the complete text must be bracketed 
so the User-process knows when it may stop reading the reply (i.e. 
stop processing input on the control connection) and go do other 
things. This requires a special format on the first line to 
indicate that more than one line is coming, and another on the 
last line to designate it as the last. At least one of these must 
contain the appropriate reply code to indicate the state of the 
transaction. To satisfy all factions, it was decided that both 
the first and last line codes should be the same. 

    Thus the format for multi-line replies is that the first line 
    will begin with the exact required reply code, followed 
    immediately by a Hyphen, "-" (also known as Minus), followed by 
    text. The last line will begin with the same code, followed 
    immediately by Space <SP>, optionally some text, and the Telnet 
    end-of-line code. 

     For example: 
          123-First line 
          Second line 
          234 A line beginning with numbers 
          123 The last line 

    The user-process then simply needs to search for the second 
    occurrence of the same reply code, followed by <SP> (Space), at 
    the beginning of a line, and ignore all intermediary lines. If 
    an intermediary line begins with a 3-digit number, the Server 
    must pad the front to avoid confusion. 

Der erste Begrüßung des Servers ist wahrscheinlich mit mehreren Leitungen sein, aber eine Antwort auf jeden Befehl kann möglicherweise mit mehreren Leitungen, so dass Sie das zu handhaben müssen.

Aber noch wichtiger ist, wenn die Fehlerprüfung zu tun, müssen Sie bei schauen nur die 3-stellige Antwortcode, nicht der Text, der sie begleitet. Mit Ausnahme von ein paar ausgewählte Befehle, wie PASV, MLST/MLSD, etc, ist der Text sonst beliebigen, kann der Server, was auch immer senden sie will. Daher müssen Sie den Text ignorieren, außer in den Fällen, in denen er tatsächlich benötigt wird, oder wenn Fehlermeldungen an den Benutzer gemeldet werden.

Probieren Sie etwas wie folgt aus:

private Socket socket; 
private BufferedReader br; 

@Test 
public void testProcessPASS() throws IOException{  

    socket = new Socket(server.getAddress(), server.getcmdPort()); 
    br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 

    this.ClientReceiveMessage(220); 
    this.ClientSendMessage("USER user_test", 331); 
    this.ClientSendMessage("PASS pass_test", 230); 
    this.ClientSendMessage("QUIT", 221); 

    socket.close(); 

    br = null; 
    socket = null; 
} 

public int ClientSendMessage(String msg, int ExpectedReplyCode) throws IOException{ 

    Writer bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); 
    bw.write(msg); 
    bw.write("\r\n"); 
    bw.flush(); 

    return ClientReceiveMessage(ExpectedReplyCode); 
} 

public int ClientReceiveMessage(int ExpectedReplyCode) throws IOException{ 

    String line = br.readLine(); 
    String msgText = msgText.substring(4); 

    if ((line.length() >= 4) && (line[3] == '-')) { 
     String endStr = line.substring(0, 2) + " "; 
     do { 
      line = br.readLine(); 
      msgText += ("\r\n" + line.substring(4)); 
     } 
     while (line.substring(0, 3) != endStr); 
    } 

    int actualReplyCode = Integer.parseInt(line.substring(0, 2)); 
    assertEquals("Response error. " + msgText, ExpectedReplyCode, actualReplyCode); 

    // TODO: if the caller wants the msgText for any reason, 
    // figure out a way to pass it back here... 

    return actualReplyCode; 
} 
+0

es liest eine Zeile zu einer Zeit, aber für jede Zeile entspricht es einer Antwort des Servers, bin ich falsch? –

+0

@AymaneBo: Ja, du liegst falsch. Manchmal kann eine einzelne Antwort ** mehr als eine ** Zeile enthalten. FTP definiert ein sehr spezifisches Format, um diese Situation zu erkennen, so dass Sie wissen, wann Sie mit dem Lesen aufhören müssen. –

+0

Sie müssen den gleichen 'BufferedReader' für die Lebensdauer des Sockets verwenden, und nachdem Sie das getan haben, können Sie auch den gleichen' PrintWriter' verwenden. – EJP

Verwandte Themen