Question

Another challenge bashed my head these days. I am trying to create a FTP Server in Java which should be able to communicate with the FileZilla FTP Client. The code isn't the best because i tried a lot of things to find out what is going on.

Here the FileZilla log translated from german

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

The main problem so far is to establish the real connection. The sockets are connected and i can send at least one message to the client back but have no chance to get the clients input e.g. for PASV, USER and 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

    }
}

For the error message/print stack:

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)
Was it helpful?

Solution 2

I still have trouble with the messaging. What i now did was to create a new thread for every connected socket as well as for every now outputstream.

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

The trouble now is that the FileZilla client won't get the 220 message.

here is the new socket handling class. i kinda copied it from the oracle page.

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

OTHER TIPS

Numerous mistakes here.

  1. Don't mess around with SO_LINGER. It isn't necessary and it only causes further unwanted pain.
  2. writeUTF() writes a format that only readUTF() can read. It's not a way to communicate with a non-Java client.
  3. You aren't dealing with accepted sockets in a separate thread, so you can only handle one client at a time.
  4. isConnected() isn't a valid test of the connection status. It tells you whether you ever connected the socket. You did, so it will return true. That won't change when the peer disconnects. You need to test for EOS in the usual ways, documented in the various read APIs.
  5. You don't need to close the listening socket when you get an IOException on an accepted socket.
  6. I don't see why an FTP server needs a GUI, but if it does it must perform all network I/O, including accepting connections, on a separate thread from the AWT thread.
  7. You only need to tell the client that he has connected successfully once, not an infinite number of times.

That's enough to be going on with.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top