【问题标题】:Java to FileZilla FTP Client: Socket write errorJava 到 FileZilla FTP 客户端:套接字写入错误
【发布时间】:2013-05-08 11:34:10
【问题描述】:

这些天,另一个挑战让我头疼。我正在尝试用 Java 创建一个应该能够与 FileZilla FTP 客户端通信的 FTP 服务器。代码不是最好的,因为我尝试了很多东西来找出发生了什么。

这里是从德语翻译过来的 FileZilla 日志

Status: Connect to 127.0.0.1:21...
Status: Connected!Waiting for welcome message.
Response:    220 localhost connected
Error:  Establishing connection to server failed.

目前的主要问题是建立真正的连接。套接字已连接,我可以向客户端发送至少一条消息,但没有机会获取客户端输入,例如用于 PASV、USER 和 PASS。

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import javax.swing.JButton;
import javax.swing.JFrame;

public class FTP_Server extends JFrame implements ActionListener, WindowListener {
    private static final long serialVersionUID = 1L;

    private Container cp;
    private JButton btncon;
    private ServerSocket listenSocket;
    private Socket connectionSocket;
    private DataOutputStream dos;
    private DataInputStream dis;
    private OutputStream os;
    private InputStream is;

    public FTP_Server() {
        super("FTP Server");

        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        cp = this.getContentPane();
        cp.setLayout(new BorderLayout());

        btncon = new JButton("Connect");
        btncon.addActionListener(this);

        cp.add(btncon, BorderLayout.CENTER);

        this.pack();
        this.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource().equals(btncon)){
            createConnection();
        }
    }

    public void createConnection() {
        System.out.println("Connecting...");
        try {
            listenSocket = new ServerSocket(21);
            listenSocket.setSoTimeout(0);
            connectionSocket = listenSocket.accept();
            connectionSocket.setSoLinger(true, 0);
            connectionSocket.setSoTimeout(0);
            connectionSocket.setKeepAlive(true);
            System.out.println("Connected!");

            os = connectionSocket.getOutputStream();
            dos = new DataOutputStream(os);

            is = connectionSocket.getInputStream();
            dis = new DataInputStream(is);

            while(connectionSocket.isConnected() == true) {
                dos.writeUTF("220 localhost connected\r\n");
                //Will cause socket write error soon!
                dos.flush();
                dos.writeUTF("331 Anonym no password needed\r\n");
                System.out.println(is.read());
            }
        } catch (SocketException exp) {
            try {
                listenSocket.close();
                System.out.println("Disconnected");
                exp.printStackTrace();
            } catch (IOException exp2) {
                exp2.printStackTrace();
            }
        } catch (IOException exp) {
            exp.printStackTrace();
        }
    }

    @Override
    public void windowOpened(WindowEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowClosing(WindowEvent e) {
        System.exit(0);
    }

    @Override
    public void windowClosed(WindowEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowIconified(WindowEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowDeiconified(WindowEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowActivated(WindowEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowDeactivated(WindowEvent e) {
        // TODO Auto-generated method stub

    }
}

对于错误信息/打印堆栈:

java.net.SocketException: Software caused connection abort: socket write error
    at java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
    at java.io.DataOutputStream.write(DataOutputStream.java:107)
    at java.io.DataOutputStream.writeUTF(DataOutputStream.java:401)
    at java.io.DataOutputStream.writeUTF(DataOutputStream.java:323)
    at ftp.FTP_Server.createConnection(FTP_Server.java:82)
    at ftp.FTP_Server.actionPerformed(FTP_Server.java:58)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6505)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
    at java.awt.Component.processEvent(Component.java:6270)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Component.dispatchEventImpl(Component.java:4861)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
    at java.awt.Container.dispatchEventImpl(Container.java:2273)
    at java.awt.Window.dispatchEventImpl(Window.java:2719)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:723)
    at java.awt.EventQueue.access$200(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:682)
    at java.awt.EventQueue$3.run(EventQueue.java:680)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:696)
    at java.awt.EventQueue$4.run(EventQueue.java:694)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:693)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)

【问题讨论】:

  • 在客户端 (Filezilla) 上看起来如何?
  • 更新了主要问题 ;)
  • 我的猜测是 Filezilla 断开了连接,不理解您的欢迎信息。但我不明白为什么会这样。奇怪的是,我没有在 Filezilla 代码中找到“建立与服务器的连接失败”的消息。您使用的是最新版本的 Filezilla 吗?我不知道 Java,所以不确定writeUTF 是否做了一些奇怪的事情(正如@EJP 建议的那样)。但是,如果它只是以 UTF 编码写入字符串,则不应该受到伤害,因为字符串是纯 ASCII。无论如何,您可以尝试改用writeChars
  • 顺便说一句,你可以试试WinSCP 并分享它的日志(结束)吗?知道得更好,我可以告诉你更多。

标签: java sockets ftp filezilla


【解决方案1】:

这里有很多错误。

  1. 不要乱用 SO_LINGER。这没有必要,只会导致更多不必要的痛苦。
  2. writeUTF() 写入只有 readUTF() 可以读取的格式。这不是一种与非 Java 客户端通信的方式。
  3. 您没有在单独的线程中处理接受的套接字,因此您一次只能处理一个客户端。
  4. isConnected() 不是对连接状态的有效测试。它会告诉您是否曾经连接过套接字。你做到了,所以它会返回true。当对等方断开连接时,这不会改变。您需要以各种读取 API 中记录的常用方式测试 EOS。
  5. 当您在接受的套接字上收到 IOException 时,无需关闭侦听套接字。
  6. 我不明白为什么 FTP 服务器需要 GUI,但如果需要,它必须在与 AWT 线程不同的线程上执行所有网络 I/O,包括接受连接。
  7. 你只需要告诉客户端他连接成功一次,而不是无限次。

这就够了。

【讨论】:

  • 我几乎认为问题来自于线程分别缺少线程。该 gui 仅适用于我,因为如果要懒惰地进行控制台工作,那我真傻。到目前为止,感谢您提供有关套接字的 cmets。我从来没有学习过如何在 java 中使用它们。
  • 我认为这与线程无关。虽然我不了解 Java,但我相信我对代码的理解足以说明,它不相关。这并不意味着您的代码在线程方面一般都可以。这不是您当前问题的原因。
  • EOS 代表什么?服务结束?
  • EOS = 流结束。 这个问题与线程无关,但是,除非您希望您的服务器完全无用,否则您将不得不对其进行多线程处理。例如 FileZilla 同时打开多个连接。如果你想要一个 GUI,也一样:你不能在 AWT 线程上执行阻塞操作。
【解决方案2】:

我仍然无法发送消息。我现在所做的是为每个连接的套接字以及现在的每个输出流创建一个新线程。

package ftp;

import java.io.DataOutputStream;
import java.io.IOException;

public class MultiIO implements Runnable{
    private DataOutputStream dos;

    public MultiIO(DataOutputStream stream) {
        dos = stream;
        System.out.println("Set OutputStream.");
    }

    @Override
    public void run() {
        try {
            dos.writeBytes("220");
            dos.flush();
            System.out.println("tried once");
        } catch (IOException e) {
            System.exit(-1);
        }
    }
}

现在的问题是 FileZilla 客户端收不到 220 消息。

这是新的套接字处理类。我有点从 oracle 页面复制它。

package ftp;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;

public class MultiConnection implements Runnable{
    private Socket cSocket;

    public MultiConnection(Socket client) {
        cSocket = client;
        System.out.println("Connected!");
    }

    @Override
    public void run() {
        OutputStream os = null;
        DataOutputStream dos = null;
        try {
            os = cSocket.getOutputStream();
            dos = new DataOutputStream(os);
        } catch (SocketException es) {
            es.printStackTrace();
        } catch (IOException e) {
            System.exit(-1);
        }

        MultiIO mio;
        mio = new MultiIO(dos);
        Thread thio = new Thread(mio);
        thio.start();
    }
}

【讨论】:

  • 一个错误是忘记了消息中的\n。
  • 如果这是一个问题,请将其作为问题发布,而不是作为答案发布。这样你不会得到任何回应。我建议你用这个“响应”替换原始问题并删除响应。
  • @Jack 这对我不起作用,我收到消息“连接超时”有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-27
  • 2013-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多