繁体   English   中英

通过同一套接字的多个消息不起作用

[英]multiple messages through the same socket doesn't work

我在简单的ftp服务器上工作,客户端必须向服务器发送多个消息,并且对于每条消息,服务器都会向客户端发送一个anwser。 当客户端发送一条消息时,它可以正常工作,并且服务器可以毫无问题地响应,例如,当客户端发送“ USER username”时,服务器将“需要密码”发送回客户端。

但是,当客户端发送另一个消息“ PASS password”(使用同一套接字)时,它将不起作用! 仅第一个交换有效(用于用户名),发送第一条消息时,服务器无任何问题,但在希望发送第二条消息(用于密码)时阻塞。

请任何人可以帮助我吗? 谢谢 !!

这是我的代码:

 @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; 
}

这是服务器代码:

 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;
        }
    }
}

}

这是处理客户端请求并向客户端发送消息并在新线程上运行的类:

 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
}


}

您的代码存在一些问题。

ClientSendMessage()使用PrintWriter.println() ,该输出输出换行符。 但是您的输入字符串上已经有换行符,因此println()正在发送额外的换行符。 此外,换行符println()输出取决于平台,而FTP具体使用CRLF 因此,您根本不应该使用println()

ClientReceiveMessage()不考虑多行响应。 根据RFC 959第4.2节“ FTP替换”:

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.

服务器的初始问候语可能是多行的,但是对任何命令的任何响应都可能是多行的,因此您需要进行处理。

但更重要的是,在做错误检查的时候,你需要看只有 3位数响应代码,而不是随之而来的文本。 除了一些选择命令(例如PASVMLST / MLSD等)之外,文本是任意的 ,服务器可以发送任何所需的内容。 因此,除了实际需要或向用户报告错误消息的情况以外,您需要忽略文本。

尝试更多类似这样的方法:

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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM