java代码实现FTP协议

2020-03-17 17:35:40 浏览数 (1)

前几节我们完成了ftp协议的主要讲解,同时使用wireshark抓包了解ftp数据协议包的特征,本节我们使用代码完成ftp协议,代码将模仿ftp客户端,它与服务器建立连接后,使用用户名和密码登陆服务器,然后获得服务器的当前目录内容,继而通过数据连接获取服务器推送目录具体信息,最后客户端关闭,下面我们看看具体的代码实现,首先在工程目录下新建名为FTPClient的类,相关实现如下:

代码语言:javascript复制
package Application;

import java.net.InetAddress;

import utils.IFTPDataReceiver;
import utils.ITCPHandler;

public class FTPClient implements ITCPHandler, IFTPDataReceiver{
    private  TCPThreeHandShakes  tcp_socket = null;
    private  int data_port = 0;
    private FTPDataReceiver data_receiver = null;
    private String server_ip;
    @Override
    public void connect_notify(boolean connect_res) {
         if (connect_res == true) {
             System.out.println("connect ftp server ok!");
         }
    }

    @Override
    public void send_notify(boolean send_res, byte[] packet_send) {
        // TODO Auto-generated method stub

    }

    @Override
    public void recv_notify(byte[] packet_recv) {
        try {
            String server_return = new String(packet_recv, "ASCII");
            System.out.println("receive info from ftp server: "    server_return);
            String return_code = server_return.substring(0, 3);
            String return_str = server_return.substring(3);
            if (return_code.equals("220")) {
                System.out.println("receive code 220: "   return_str);
                send_command("USER chenyirn");
            }
            if (return_code.equals("331")) {
                System.out.println("receive code 331: "   return_str);
                //服务器请求用户名密码
                send_command("PASS 1111rn");
            }
            if (return_code.equals("230")) {
                System.out.println("receive code 230: "   return_str);
                //用户登录成功
                send_command("PWDrn"); //获取服务器文件目录
            }
            if (return_code.equals("257")) {
                System.out.println("receive code 257: "   return_str);
                send_command("PASVrn");
            }
            if (return_code.equals("227")) {
                System.out.println("receive code 227: "   return_str);
                int ip_port_index = return_str.indexOf("(");
                String port_str = return_str.substring(ip_port_index);
                int ip_count = 4; //经过4个逗号就能找到端口
                while (ip_count > 0) {
                    int idx = port_str.indexOf(',');
                    ip_count--;
                    port_str = port_str.substring(idx   1);
                }
                int idx = port_str.indexOf(',');
                String p1 = port_str.substring(0, idx);
                port_str = port_str.substring(idx   1);
                idx = port_str.indexOf(')');
                String p2 = port_str.substring(0, idx);
                int port = Integer.parseInt(p1) * 256   Integer.parseInt(p2);
                System.out.println("get data port : "   port);
                data_port = port;
                send_command("TYPE Arn"); //通知服务器以ASCII的方式传输数据
            }
            if  (return_code.equals("200")) { //服务器同意使用ASCII方式传递数据
                System.out.println("receive code 200: "   return_str);
                send_command("LISTrn");//要求服务器传输当前目录下的文件信息
                data_receiver = new FTPDataReceiver(server_ip, data_port, this);
                data_receiver.get_data();
            }
            if (return_code.equals("150")) { //服务器通知数据发送完毕
                System.out.println("receive code 150: "   return_str);
                tcp_socket.tcp_close();
            }
            //tcp_socket.tcp_close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    private void send_command(String command) {
        try {
            tcp_socket.tcp_send(command.getBytes());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void connect_close_notify(boolean close_res) {
        // TODO Auto-generated method stub

    }

    public void run() {
         try {
            InetAddress ip = InetAddress.getByName("192.168.2.127"); //连接ftp服务器
            server_ip = "192.168.2.127";
            short port = 20000;
            tcp_socket = new TCPThreeHandShakes(ip.getAddress(), port, this);
            tcp_socket.tcp_connect();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void receive_ftp_data(byte[] data) {
        System.out.println("Successfuly get ftp data");
        String ftp_data = new String(data);
        System.out.println("content of ftp_data: "   ftp_data);
    }

}

代码实现中recv_notify用来解读服务器返回的信息,前三个字符是服务器的返回码,后面字符串是对返回码的解释。这里值得关注的是当客户端向服务器发送PSAV命令后,服务器返回码为227,其中的字符串包含了用于数据传输的端口,代码需要解读返回字符串,然后计算出端口,并像服务器发送TYPE A命令告诉服务器通过ASCII模式传输数据。最后启动新的tcp连接去接收数据。一旦在数据端口与服务器实现三次握手后,服务器会主动给我们推送数据。在完成PSAV命令后,代码向服务器发送LIST命令,要求服务器给出当前目录下的所有文件信息,然后代码创建FTPDataReceiver实例,该对象负责通过数据端口与服务器连接,同时等待服务器推送数据,接收完数据后他把接收到的内容推送给FTPClient对象,我们看FTPDataReceiver的实现:

代码语言:javascript复制
package Application;

import java.net.InetAddress;
import java.nio.ByteBuffer;

import utils.IFTPDataReceiver;
import utils.ITCPHandler;

public class FTPDataReceiver implements ITCPHandler{
    private int data_port = 0;
    private IFTPDataReceiver data_receiver = null;
    private  TCPThreeHandShakes  tcp_socket = null;
    private String server_ip = "";
    private byte[] data_buffer = new byte[4096];
    private ByteBuffer byteBuffer = null;
    public FTPDataReceiver(String ip, int port, IFTPDataReceiver receiver) {
        this.data_port = port;
        this.data_receiver = receiver;
        this.server_ip = ip;
        byteBuffer = ByteBuffer.wrap(data_buffer);
    }


    public void get_data() {
         try {
             InetAddress ip = InetAddress.getByName(server_ip); //连接ftp服务器
             tcp_socket = new TCPThreeHandShakes(ip.getAddress(), (short)data_port, this);
             tcp_socket.tcp_connect();
         } catch (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
    }

    @Override
    public void connect_notify(boolean connect_res) {
        if (connect_res) {
            System.out.println("ftp data connection ok");
        } else {
            System.out.println("ftp data connection fail");
        }

    }

    @Override
    public void send_notify(boolean send_res, byte[] packet_send) {
        // TODO Auto-generated method stub

    }

    @Override
    public void recv_notify(byte[] packet_recv) {
        System.out.println("ftp receiving data");
        byteBuffer.put(packet_recv);
    }

    @Override
    public void connect_close_notify(boolean close_res) {
        try {
            tcp_socket.tcp_close();
            data_receiver.receive_ftp_data(byteBuffer.array());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

完成上面代码后运行,我们可以得到如下结果:

从图中可以看到,我们代码成功接收了ftp服务器推送的目录信息。更多详细讲解和代码调试演示请点击’阅读原文‘。

0 人点赞