java基于Socket实现一个client/server模式的用于文件传输的程序

需求分析:

在学习到与linux相关的知识时,不可避免要使用到虚拟机,但是虚拟机与我们的物理机是相对独立的,两个系统的文件不能实现很好的共享。虽然可以通过共享文件夹或Xftp工具来帮助我们解决这个问题,但是使用这些方法也会带来不少问题,例如前者不仅操作繁琐,而且容易失败,二后者需要付费或者注册。基于这些我开发了这个程序用于日常的文件传输。

架构设计:

由于采用了c/s模式,我们需要分别在自己的物理机和虚拟机上运行两个程序。

这里我采用的是物理机运行客户机程序,而虚拟机运行服务器程序,因为有些虚拟机并没有图形化界面,很难通过网络传输源文件,代码就需要手敲,服务器程序的代码相对较少。

客户机程序设计:

 

服务器程序设计:

 实现代码:

服务器程序

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class FtpServer {

    FtpServer(){

        try{

            ServerSocket server = new ServerSocket(8888);
            Socket socket = new Socket();
            //表示接收文件时的临时编号
            int count=1;

            //监听请求,用户客户机判断是否能连接
            socket = server.accept();
            socket.close();

            while(true){

                //接收后缀
                socket = server.accept();
                String suffix = null;
                InputStream is = socket.getInputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                suffix = br.readLine();
                br.close();
                socket.close();

                //创建File实例对象
                File file = new File("/home/tmp_download/tmp"+ count +"."+suffix);
                count ++;
                if(!file.exists()){
                    file.createNewFile();
                }
                OutputStream os = new FileOutputStream(file);

                //接收文件信息
                socket = server.accept();
                byte[] b = new byte[1024];
                int len = 0;
                is = socket.getInputStream();
                while((len = is.read(b)) > 0){
                    os.write(b,0,len);
                }

                System.out.println("成功接收文件,临时文件名为tmp" + (count-1) + "." + suffix);

            }

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

    }

    public static void main(String[] args) {
        FtpServer ftpServer = new FtpServer();
    }
}

客户机程序

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Scanner;

public class FtpClient {

    Scanner scanner = new Scanner(System.in);
    String path = null;
    File file = null;
    String ip = null;
    String port = null;
    //创建一个套接字实例对象
    Socket socket = null;
    FtpClient() throws Exception {

        boolean run = true;
        while(run){

            try{
                System.out.print("请输入要传输文件的目的主机ip:");
                ip = scanner.next();
                System.out.print("请输入端口:");
                port = scanner.next();

                socket = new Socket();
                SocketAddress socketAddress = new InetSocketAddress(ip, Integer.valueOf(port));
                socket.connect(socketAddress,3*3000);
                run = false;
                socket.close();
            }catch (Exception e){
                System.out.println("连接超时!");
            }

        }
        ftpRun();
    }

    public String ftpRun() throws Exception {
        path = "C:\\";
        file = new File(path);
        String command = null;

        while(true){
            System.out.print(path+" > ");
            command = scanner.next();
            if(command.equals("break")){//退出
                break;
            }else if(command.equals("ls")){//显示当前目录下的文件和目录
                ls();
            }else if(command.equals("cd")){//打开文件和目录
                cd();
            }else if(command.equals("change")){//输入盘符字符,盘符
                change();
            }else if(command.equals("path")){//输入绝对路径,进入该目录或打开该文件
                path();
            }else if(command.equals("send")){//发送文件
                send();
            }else if(command.equals("help")) {//获取帮助
                help();
            }
            System.out.println();
        }

        return path;
    }

    //列出当前目录下的所有目录和文件
    private void ls(){
        //获取当前目录下的文件和目录
        String[] directorys = file.list();
        //打印输出
        for(String dir:directorys){
            if(new File(path+"\\"+dir).isDirectory()){//是目录
                System.out.println("dir:"+dir);
            }else{//是文件
                System.out.println("file:"+dir);
            }
        }
    }

    //打开当前目录下目录或者文件,..为返回上一级目录
    private void cd() throws Exception {
        String open = null;
        System.out.print("file or directory:");
        open = scanner.next();

        //判断是否返回上一级目录
        if(open.equals("..")){
            path = file.getParent();
        }else{
            path = path + "\\" + open;
        }

        file = new File(path);
        //判断文件或目录是否存在
        if(!file.exists()){
            throw new Exception("文件不存在");
        }
    }

    //输入盘符字符,改变盘符
    private void change(){
        String change = null;
        System.out.println("drive letter:");
        change = scanner.next();
        path = change + ":\\";
        file = new File(path);
    }

    //输入绝对路径,进入对应目录或打开对应文件
    private void path() throws Exception {
        String tmpPath = null;
        System.out.println("absolute:");
        tmpPath = scanner.next();
        File tmpFile = new File(tmpPath);
        if(!tmpFile.exists()){
            throw new Exception("目录或文件不存在");
        }
        path = tmpPath;
        file = tmpFile;
    }

    //发送文件
    private void send() throws Exception {
        //根据path创建File实例
        file = new File(path);

        //查看文件是否存在
        if(!file.exists()){
            throw new Exception("文件不存在!");
        }
        //确认是否是文件
        if(!file.isFile()){
            throw new Exception("这不是一个文件!");
        }
        //发送后缀
        sendSuffix(path);
        //发送文件
        sendFile(file);
    }

    //帮助
    private void help(){
        //存储提示信息
        String[] tips = {
                "break 退出",
                "help 帮助",
                "ls 查看当前目录下的目录或文件",
                "change 改变盘符(输入单个盘符字母就行,例如C)",
                "cd 打开当前目录下的文件或目录(输入目录名或文件名即可,例如document,test.txt",
                "send 发送文件",
                "path 输入绝对路径即可进入该路径,或打开该文件"
        };
        //打印所有提示
        for(String tip:tips){
            System.out.println(tip);
        }
    }

    //发送后缀
    public void sendSuffix(String path){
        try{
            //文件的后缀
            String suffix = null;
            //获取后缀
            suffix = getSuffix(path);
            System.out.println(suffix);
            //接收Socket
            Socket tmpSocket = new Socket(ip,Integer.valueOf(port));
            //获取输出流
            OutputStream os = tmpSocket.getOutputStream();
            //创建字符输出流
            PrintWriter pw = new PrintWriter(os);
            pw.println(suffix);
            pw.flush();

            pw.close();
            os.close();
            tmpSocket.close();
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    //根据路径获取后缀
    private String getSuffix(String path){
        String[] split = path.split("\\.");
        return split[split.length - 1];
    }

    //发送文件数据
    public void sendFile(File file){
        try{
            Socket tmpSocket = new Socket(ip,Integer.valueOf(port));
            //获取输出流
            OutputStream os = tmpSocket.getOutputStream();

            //以二进制的形式发送文件信息
            InputStream is = new FileInputStream(file);
            byte[] b = new byte[1024];
            int len = 0;
            while((len = is.read(b)) > 0){
                os.write(b,0,len);
            }

            //提示文件传输情况
            System.out.println("文件传输成功!");

            is.close();
            os.close();
            tmpSocket.close();
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public static void main(String[] args) throws Exception {
        FtpClient ftpClient = new FtpClient();
    }

}

实际测试

本次测试,将在windows物理机中将指定目录下的test.txt,test.docx,test.html依次传入到ubuntu虚拟机中。

1、创建test.txt.test.docx,test.html

 2、打开服务器程序

 3、打开客户机程序

 4、在客户机程序中发送文件到虚拟机中

 5、查看虚拟机中的文件

 

 test.docx中的数据传输成功

 经验总结

在实现过程中遇到的问题及解决方法

1)

问题:传输文件时,服务器没有接收到客户机传输的文件的后缀名

解决方法:我仔细查看代码,发现我客户机请求连接了三次,而服务器只监听了两次,导致接收乱套了。服务器多监听一次就好了。

2)

问题:客户机发送文件后缀名后报错,说socket已关闭

解决方法:查看代码后发现,我使用的是同一个socket实例,必须使用不同的实例才行,如果同时使用一个实例一起传输数据容易混在一起,如果分开传输,就必须关闭流,否则会阻塞,但关闭流后会报错,所以要使用不同的实例。

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您提供一份基本的Java代码实现: 1. 客户端界面设计 客户端界面采用字符界面,使用Java中的Console类来实现。客户端界面需要包括以下功能: - 连接服务器 - 上传文件 - 下载文件 - 查看服务器上的文件列表 - 断开连接 以下是客户端界面的代码实现: ``` import java.io.BufferedReader; import java.io.Console; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; public class Client { private Socket socket; private BufferedReader reader; private Console console; public Client() { console = System.console(); } public void start() { while (true) { String command = console.readLine("请输入命令:"); if (command.equals("connect")) { connect(); } else if (command.equals("upload")) { upload(); } else if (command.equals("download")) { download(); } else if (command.equals("list")) { list(); } else if (command.equals("disconnect")) { disconnect(); break; } else { System.out.println("无效命令,请重新输入!"); } } } private void connect() { try { String host = console.readLine("请输入服务器地址:"); int port = Integer.parseInt(console.readLine("请输入服务器端口号:")); socket = new Socket(host, port); reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println("连接成功!"); } catch (IOException e) { System.out.println("连接失败!"); e.printStackTrace(); } } private void upload() { // TODO: 实现文件上传 } private void download() { // TODO: 实现文件下载 } private void list() { // TODO: 实现查看服务器上的文件列表 } private void disconnect() { try { socket.close(); reader.close(); System.out.println("已断开连接!"); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Client client = new Client(); client.start(); } } ``` 2. 服务器端实现 服务器端需要实现以下功能: - 监听客户端连接请求 - 接收客户端上传的文件并保存到指定目录 - 发送指定目录下的文件列表给客户端 - 接收客户端下载的文件并发送给客户端 以下是服务器端的代码实现: ``` import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class Server { private ServerSocket serverSocket; private List<String> fileList; public Server(int port) { try { serverSocket = new ServerSocket(port); fileList = new ArrayList<>(); System.out.println("服务器已启动,等待客户端连接..."); } catch (IOException e) { e.printStackTrace(); } } public void start() { while (true) { try { Socket socket = serverSocket.accept(); System.out.println("客户端已连接:" + socket.getInetAddress()); new Thread(new ClientHandler(socket)).start(); } catch (IOException e) { e.printStackTrace(); } } } private synchronized void addFile(String filename) { fileList.add(filename); } private synchronized void removeFile(String filename) { fileList.remove(filename); } private synchronized List<String> getFileList() { return new ArrayList<>(fileList); } private class ClientHandler implements Runnable { private Socket socket; private BufferedReader reader; private PrintWriter writer; public ClientHandler(Socket socket) { try { this.socket = socket; reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { try { while (true) { String command = reader.readLine(); if (command == null) { break; } if (command.equals("upload")) { receiveFile(); } else if (command.equals("download")) { sendFile(); } else if (command.equals("list")) { sendFileList(); } else { writer.println("无效命令!"); writer.flush(); } } } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); reader.close(); writer.close(); System.out.println("客户端已断开连接:" + socket.getInetAddress()); } catch (IOException e) { e.printStackTrace(); } } } private void receiveFile() throws IOException { String filename = reader.readLine(); addFile(filename); FileOutputStream fos = new FileOutputStream(filename); byte[] buffer = new byte[1024]; int len; while ((len = socket.getInputStream().read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.close(); removeFile(filename); System.out.println("已接收文件:" + filename); } private void sendFile() throws IOException { String filename = reader.readLine(); File file = new File(filename); if (!file.exists() || !file.isFile()) { writer.println("文件不存在!"); writer.flush(); return; } FileInputStream fis = new FileInputStream(file); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { socket.getOutputStream().write(buffer, 0, len); } fis.close(); System.out.println("已发送文件:" + filename); } private void sendFileList() throws IOException { List<String> fileList = getFileList(); for (String filename : fileList) { writer.println(filename); } writer.flush(); System.out.println("已发送文件列表!"); } } public static void main(String[] args) { Server server = new Server(8888); server.start(); } } ``` 以上是一个基本的Java代码实现,可以根据实际需求进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值