专栏首页Coding迪斯尼java代码实现FTP协议

java代码实现FTP协议

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

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 chenyi\r\n");
            }
            if (return_code.equals("331")) {
                System.out.println("receive code 331: " + return_str);
                //服务器请求用户名密码
                send_command("PASS 1111\r\n");
            }
            if (return_code.equals("230")) {
                System.out.println("receive code 230: " + return_str);
                //用户登录成功
                send_command("PWD\r\n"); //获取服务器文件目录
            }
            if (return_code.equals("257")) {
                System.out.println("receive code 257: " + return_str);
                send_command("PASV\r\n");
            }
            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 A\r\n"); //通知服务器以ASCII的方式传输数据
            }
            if  (return_code.equals("200")) { //服务器同意使用ASCII方式传递数据
                System.out.println("receive code 200: " + return_str);
                send_command("LIST\r\n");//要求服务器传输当前目录下的文件信息
                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的实现:

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服务器推送的目录信息。更多详细讲解和代码调试演示请点击’阅读原文‘。

本文分享自微信公众号 - Coding迪斯尼(gh_c9f933e7765d),作者:陈屹

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一个有趣的网络程序TraceRoute:记录数据包传送路径上的路由器IP

    在大多数操作系统上都附带一个网络程序叫TraceRoute,它的作用是追踪数据包发送到指定对象前,在传送路径上经过了几个路由器转发,下图是用TraceRoute...

    望月从良
  • 发刊词:使用神经网络玩转图像和自然语言识别

    望月从良
  • 启动网络的自我训练流程,展示网络数字图片识别效果

    望月从良
  • javascript设计模式 -- 工厂模式

    工厂模式哈,看了半天感觉大概意思就是说,有这么个函数,它会创建什么样的实例出来, 完全是取决于你传了什么样的参数进去。 创建出来的这些实例,都拥有相同的接口,就...

    web前端教室
  • R语言可视化——地图填充与散点图图层叠加

    今天跟大家分享关于如何在地图图层上添加散点图。 散点图需要精确的经纬度信息才能在叠加的图层上进行映射,因此我们选用中国省级轮廓地图以及各省省会城市的经纬度进行案...

    数据小磨坊
  • 马化腾:电力时代孕育了计算机,人工智能兴盛于云计算

    6月21日,“云+未来”峰会在深圳召开。在数字经济高速发展,人工智能一日千里的时代,云计算为社会和经济带来的量变与质变究竟是什么?腾讯董事会主席兼首席执行官马化...

    云加创业小助手
  • 科大讯飞年度发布会:讯飞超脑摘取认知智能桂冠,输入法领衔AI+ (刘庆峰、胡郁演讲实录)

    【新智元导读】作为中国人工智能产业的标杆企业,科大讯飞2016年度发布会受到业界高度瞩目。11月23日北京国家会议中心,科大讯飞携产业链合作伙伴的人工智能+黑科...

    新智元
  • 马化腾:电力时代孕育了计算机,人工智能兴盛于云计算

    6月21日,“云+未来”峰会在深圳召开。在数字经济高速发展,人工智能一日千里的时代,云计算为社会和经济带来的量变与质变究竟是什么?腾讯董事会主席兼首席执行官马化...

    云加社区
  • 挑战程序竞赛系列(40):4.1模运算的世界(3)

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    用户1147447
  • [享学Netflix] 二十八、Hystrix事件计数器EventCounts和执行结果ExecutionResult

    代码下载地址:https://github.com/f641385712/netflix-learning

    YourBatman

扫码关注云+社区

领取腾讯云代金券