8

我的问题是我正在创建一个 FTP 客户端,到目前为止,除了一个小细节之外,它的工作完美无缺,这一直困扰着我。我需要知道 FTP 欢迎消息跨越了多少行......这是不能接受的!

    private Socket connection;
    private PrintWriter outStream;
    private Scanner inStream;

public void InitiateConnection() throws IOException 
{
    log.Info(this, "Initiating connection to host: " + host + ":" + port);
    connection = new Socket(host, port);
    log.Info(this, "Connection initiated.");
    outStream = new PrintWriter(connection.getOutputStream(), true);
    inStream = new Scanner(connection.getInputStream());
    Listen();
    Listen();    
    Listen();
}

public String Listen() throws IOException
{
    if(connection == null)
        throw new IOException("Connection not initiated yet");
    String response = inStream.nextLine();
    log.Info(this, "Response: " + response);
    return response;
}

这是简单的设置,我省略了所有其他代码,因为它与我的问题没有任何关系。

我尝试了多种方法来实现这一目标。失败的解决方案1:

String response = "";
while(response != null)
    Listen();

失败的解决方案2:

while(connection.getInputStream().available > 0)
    Listen();

还有无数其他的......但要么它不起作用,要么方法阻塞并等待新的输入。我什至尝试过超时,但这也不能完美地工作,它不是解决这个问题的正确方法......

I need to be able to get the entire welcome message from the FTP server, without knowing the amount of lines... So I can both get this:

Response:   220-FileZilla Server version 0.9.39 beta
Response:   220-written by Tim Kosse (Tim.Kosse@gmx.de)
Response:   220 Please visit http://sourceforge.net/projects/filezilla/

And this:

Response:   220-FileZilla Server version 0.9.40 beta
Response:   220 Welcome to Andrés FTP Server
4

2 回答 2

8

If you have a close look at the messages, you see that all but the last lines have a - behind the status code. The last line has a , however, indicating, well, the last line.

You can read that in RFC 959, section 4.2:

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 , optionally some text, and the Telnet end-of-line code.

There is nothing said about the 2nd to second-last line, but it is logical that they have the same format as the 1st one.


Update: The FTP protocol seems to be badly documented, but I found another reference stating the same as me above:

The TCP/IP Guide mentions that

It is possible for a reply to contain more than one line of text. In this case, each line starts with the reply code, and all lines but the last have a hyphen between the reply code and the reply text, to indicate that the reply continues. The last line has a space between the reply code and reply text, just like a single-line reply. This facility is often used to provide additional response information after a user logs in, via the 230 reply code.

于 2012-10-26T08:05:58.603 回答
0

Have you tried like this?

    StringBuilder response = new StringBuilder();
    BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    do {            
        response.append(br.readLine());                                     
    }
    while (br.ready());

Whereas the BufferedReader's ready() method:

Tells whether this stream is ready to be read. A buffered character stream is ready if the buffer is not empty, or if the underlying character stream is ready.
Returns:
True if the next read() is guaranteed not to block for input, false otherwise. Note that returning false does not guarantee that the next read will block.

It is actually a Reader's method.

UPDATE:

As glglgl pointed out there are two types of responses in FTP:

  • "single line" response (my term)
  • multiline response (spec term)

As I said in comment, fire up FIlleZilla, connect and observe the log (preferably comparing the commands in output with spec defintion) and you will see why the "single line" is quoted.

On some FTP server implementations the above code may work, but it will surley not work on all implementations, since it does not implement the client part of the protocol correctly. This is the updated version:

String response;
List<String> responseList = new ArrayList<String>();  
boolean isMultilineStart = false, isMultilineEnd = false;
String mlCode = null;
do {            
   responseList.add(br.readLine());                           
   if (!isMultilineStart) {
        isMultilineStart = responseList.get(responseList.size()-1).matches("\\d\\d\\d-.*");                
        mlCode = responseList.get(responseList.size()-1).substring(0,3);
   }
   else {
       isMultilineEnd = responseList.get(responseList.size()-1).startsWith(mlCode+" ");
   }
}
while (br.ready() || (isMultilineStart && !isMultilineEnd));   
response = Arrays.deepToString(responseList.toArray());
于 2012-10-26T07:37:52.387 回答