940 views

Java Socket 实现 FTP

好久没更新了,实在是太忙了,各种活动,可什么都不给力呐,各种郁闷唉.

可博客不更新也不行呐,就把计算机网络第一次大作业拿出来,是基于JavaSocket网络编程,实现简单的FTP协议,包括主动模式(port)和被动模式(pasv)还挺不错的,对于理解FTP协议效果非常好!

普通FTP命令实现非常简单,如:”pwd“,”mkdir“,”rmdir“,”cwd“,”cd“,”dele“,”syst“,”noop“,”quit“及”help“,这些命令实现完全一致.

难点在于主动模式被动模式的实现,上传(stor)下载(retr),list,nlst都要用到主动模式或者是被动模式.

代码如下:

package info.yilee.ftpclient;

//import 省略
public class FtpClient {

    Socket socket; // 客户端命令socket
    BufferedReader in; // 客户端命令输入流
    PrintWriter out; // 客户端命令输出流
    Socket datasocket; // 客户端数据socket
    BufferedReader dataIn; // 客户端数据输入流
    PrintWriter dataOut; // 客户端数据输出流
    BufferedReader line;
    String ftpServerIP; // 服务器地址
    ServerSocket serverSocket;

    public FtpClient(String ftpServerIP) {
        try {
            /**
             * 初始化socket连接
             * 根据服务器端IP地址和命令端口初始化命令socket连接
             * 并获取命令输入输出流
             */
            this.ftpServerIP = ftpServerIP;
            socket = new Socket(ftpServerIP, 21);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream());

            // 初始化命令接口
            String s = in.readLine();
            System.out.println(s);
            line = new BufferedReader(new InputStreamReader(System.in));

            System.out.println(“请输入用户名”);

            // 从 控制台读入用户名密码
            String username = line.readLine();
            System.out.println(“请输入密码”);
            String passwd = line.readLine();
             // 用户登录操作
            user(username, passwd);
        }// try
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void run() {

        while (true) {
        try {

                System.out.println(“请输入命令”);
                String methodName = line.readLine();

                Method method = this.getClass().getMethod(methodName, null);
                method.invoke(this, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /** 登录
     *  命令格式为 user <username>
     *  命令格式为 pass <password>
     */
    public void user(String username, String passwd) {

        try {
            // 用户名登录
            out.println(“user “ + username);
            out.flush();
            // 使用字符串S从服务器端读入返回状态码,并在控制台打印
            String s = null;
            s = in.readLine();
            System.out.println(s);
            // 密码输出
            out.println(“pass “ + passwd);
            // pass命令
            // s = 读入返回状态并打印
            out.flush();
            s = in.readLine();
            System.out.println(s);

        } catch (Exception e) {

            e.printStackTrace();
        }

    }// user

    //只实现一个命令 其他命令完全一致!
    // mkdir 命令- 实现在服务器端创建文件夹命令,
    public void mkdir() {
        try {

            System.out.println(“请输入文件夹名称”);
            String dirname = line.readLine();
            out.println(“mkd “ + dirname);
            out.flush();
            String s = null;
            s = in.readLine();
            System.out.println(s);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }// mkdir

    // list 命令 — 实现目录文件查询功能
    // 提示: 需要用到数据连接,请编码实现并打印返回目录下文件信息
    public void list() {
        try {

            System.out.println(“请输入文件夹名称”);
            String dirname = line.readLine();
            pasv();//被动链接
            /**
             * 数据连接命令
             */
            out.println(“list “ + dirname);
            out.flush();
            /**
             * 数据获取,并打印
             */
            char[] buf = new char[10000];
            dataIn.read(buf, 0, buf.length);
            System.out.println(buf);

            /**
             * 关闭数据读入和输出流
             */
            datasocket.close();
            dataOut.close();
            dataIn.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // nlst 命令 — 与List命令相同,请使用和list命令不同的数据连接命令,并使用nlst命令
    public void nlst() {

        try {
            port();//主动连接
            System.out.println(“请输入文件夹名称”);
            String dirname = line.readLine();
            out.println(“nlst “ + dirname);
            out.flush();
            datasocket = serverSocket.accept();
            dataIn = new BufferedReader(new InputStreamReader(datasocket.getInputStream()));

            /**
             * 数据获取,并打印
             */
            char[] buf = new char[20000];
            dataIn.read(buf, 0, buf.length);
            System.out.println(buf);
            /**
             * 关闭数据读入和输出流
             */
            datasocket.close();
            dataIn.close();

        } catch (IOException e) {

            e.printStackTrace();
        }
    }

    // stor 命令 — 文件上传命令
    public void stor() {

        try {

            System.out.println(“请输入文件名称”);
            String filename = line.readLine();
           
            /**
             * 建立数据连接
             */
            // port();//住动连接
            pasv();
            // 文件操作
            File file = new File(filename);
            FileInputStream fi = new FileInputStream(file);
            byte[] readByte = new byte[1024];
            out.println(“stor “ + file.getName());
            out.flush();
            OutputStream bo = datasocket.getOutputStream();

            while (fi.read(readByte) > 0) {
                bo.write(readByte);
            }

            /**
             * 文件输入流关闭,数据socket端口关闭
             */
            fi.close();
            bo.close();
            System.out.println(in.readLine());
            dataOut.close();
            dataIn.close();
            datasocket.close();
            System.out.println(in.readLine());

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }// retr

    // retr 命令 — 文件下载,请参照stor命令实现,并使用和stor不同的数据连接建立命令
    // 并下载到自定义文件
    public void retr() {
        try {
            System.out.println(“请输入要下载的文件名”);
            String filename = line.readLine();
            System.out.println(“请输入保存的地址”);
            String saveAdd = line.readLine();
            port();
            // 文件操作
            File file = new File(saveAdd + filename);
            if (!file.exists()) {
                file.createNewFile();
            }
            FileOutputStream fo = new FileOutputStream(file);
            System.out.println(“”);
            out.println(“retr “ + filename);
            out.flush();
            datasocket = serverSocket.accept();

            byte[] readByte = new byte[2000];
            BufferedInputStream dataInput = new BufferedInputStream(datasocket.getInputStream());
            int n;
            while ((n = dataInput.read(readByte)) > 0) {
                fo.write(readByte, 0, n);
            }
            fo.close();
            dataInput.close();
            System.out.println(in.readLine());
            datasocket.close();
            System.out.println(in.readLine());

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }// retr

    // port 命令 — 主动建立数据连接命令请编码实现,参照pasv命令
    // 提示: 命令格式为prot (ip,ip,ip,ip,port>>8,prot&&0xff),根据服务器情况调试
    //port1*256+port
    public void port() {

        String cmd = “port “; // PORT存放用PORT命令传递数据的变量
        int i;

        try {
            // 得到自己的地址InetAddress.getLocalHost().getAddress()
            byte[] address = InetAddress.getLocalHost().getAddress();
            // 用适当的端口号构造服务器
            serverSocket = new ServerSocket(0);
            // 准备传送PORT命令用的数据
            for (i = 0; i < 4; ++i) {
                cmd = cmd + (address[i] & 0xff) + “,”;
            }
            System.out.println(cmd);
            // 利用控制用的流传送PORT命令

            int port1 = serverSocket.getLocalPort() >> 8;
            int port2 = serverSocket.getLocalPort() & 0xff;

            out.println(cmd + port1 + “,” + port2);
            out.flush();
            String s;
            s = in.readLine();
            System.out.println(s);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // pasv 命令 — 被动建立数据连接命令
    public void pasv() {
        try {
            out.println(“pasv”);
            out.flush();
            String s;
            s = in.readLine();
            System.out.println(s);
            Pattern pattern = Pattern.compile(“.+\\(\\d+,\\d+,\\d+,\\d+,(\\d+),(\\d+)\\)”);
            Matcher matcher = pattern.matcher(s);
            int port1 = 0;
            int port2 = 0;
            if (matcher.find()) {
                port1 = Integer.parseInt(matcher.group(1));
                port2 = Integer.parseInt(matcher.group(2));
            }

            int port = port1 * 256 + port2;

            datasocket = new Socket(“localhost”, port);
            dataIn = new BufferedReader(new InputStreamReader(datasocket.getInputStream()));
            dataOut = new PrintWriter(datasocket.getOutputStream());

        } catch (Exception e) {
            e.printStackTrace();
       }
    }

    public static void main(String[] args) {
        FtpClient fc = new FtpClient(“127.0.0.1″);
        fc.run();
    }
}

无觅相关文章插件,快速提升流量

5 thoughts on “Java Socket 实现 FTP

  1. 你的stor命令都没用到pasv里的dataIn 和 dataOut,其实我跟你一样,还是康晓跟我说我才发觉的。

  2. 这样就绕开了一个将字节流转换成字符流的难题,具体实现起来还是会有Bug的。不知道哪里出问题了,Debug 不出来啊。。。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>