socket编程实现ftp服务器

功能

  • 用户输入user username、pass password注册,注册后输入dir查看服务器文件列表,输入get filename path下载文件到指定路径。

思路

  1. 客户端建立连接,打开输入输出流,开循环支持多次数据交换,直到用户输入退出命令,再关闭流和连接。
  • 循环内读取用户输入,作初步检测,看是否为上面几个命令之一,否则提示“输入错误”,要求用户再次输入。
  • 若是上面几大命令之一,则将命令写到输出流,发送给服务器。
  1. 服务器监听21端口,开无限循环支持多线程,每个循环内获取当前到达的连接,并传入一个新线程内处理该连接(其实把获取连接的工作交给线程也行)。
  • 线程内先获取输入流与输出流,然后开循环支持多次数据交换,直到客户端发来的命令为退出命令,才退出循环,关闭流和连接。
  • 循环内读取用户发来的命令,分类,分别执行相应方法,生成响应码,反馈给客户端。
  1. 客户端接收服务端的反馈,根据响应码打印相应信息。

  2. 进入下一次循环,接着读取用户命令。

重点

  1. 多线程:一个服务端支持多个客户端。
  2. 多次数据交换:复用socket,服务端获取当前到达的socket后,即进行多次数据交换,知道用户退出才关闭socket,而不是一条命令开一次socket,一个socket只进行一次数据交换。
  3. 文件下载:客户端把指定文件路径发给服务端后,服务器将此路径搞成文件输入流,从该服务器的输出流写出,发送给客户端。这其实和前面反馈响应码是一样的,只是反馈的内容从字符串变成了流;然后客户端读取服务器的反馈,并写出到由指定下载路径+文件名搞成的输出流。

只实现文件下载、多线程和复用socket的简易版本

  • 客户端:这里还出现了一个致命bug,即最后再写出的时候,最后一次循环异常阻塞。

      public class FileSocketClient {
          public static void main(String[] args) {
              Socket socket=null;
              InputStream is=null;
              OutputStream os=null;
      
              //服务器ip地址
              String serverIp="127.0.0.1";
              //服务器端端口号
              int port=21;
              //下载文件
              String filename="d.txt";
              
              try {
                  //建立连接
                  socket=new Socket(serverIp,port);
                  //支持多次数据交换
                  for (int i=0; i<3; i++) {
                      //发送数据
                      os = socket.getOutputStream();
                      os.write(filename.getBytes());
                      //接收反馈数据
                      is = socket.getInputStream();
                      OutputStream fos = new FileOutputStream(filename);
                      byte[] b = new byte[1024];
                      int n = 0;
      
                      n = is.read(b);
                      String response = new String(b, 0, n);
                      String[] entry = response.split(":");
                      if (entry.length == 2) {
                          if (entry[1].equals("504"))
                              System.out.println("文件不存在!");
      
                          if (entry[1].equals("332"))
                              System.out.println("请先注册!");
                      } else {
                          while (-1 != n) {
                              //fos:外部文件
                              fos.write(b);
                              //is:服务器反馈
                              n = is.read(b); //最后一次读取后直接结束程序
                          }
      
                          System.out.println("下载成功");
                      }
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally{
                  try {
                      //关闭流和链接
                      is.close();
                      os.close();
                      socket.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
      
              }
      
          }
      }
    
  • 服务端:

      public class FileSocketServer {
    
          public static void main(String[] args) throws InterruptedException {
              ServerSocket serverSocket=null;
              Socket socket=null;
              OutputStream os=null;
              InputStream is=null;
              //监听端口号
              int port=21;
              //文件根目录
              String path="data/";
      
              try {
                  //监听端口
                  serverSocket=new ServerSocket(port);
                  //获得客户端连接;进入这一步的执行时会等待客户端
                  socket=serverSocket.accept();
                  //接收客户端发送内容
                  is=socket.getInputStream();
                  os = socket.getOutputStream();
                  byte[] b=new byte[1024];
      
                  //支持多次数据交换
                  for (int i=0; i<3; i++) {
                      int n = is.read(b);
                      String filename = new String(b, 0, n);
                      //反馈
                      String response = "status:";
                      response = download(path + filename, os);
      
                      //下载失败
                      if (response != "200") {
                          response = "status:" + response;
                          os.write(response.getBytes());
                      }
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      //关闭流和连接
                      is.close();
                      os.close();
                      socket.close();
                      serverSocket.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
      
              }
          }
      
          private static String download(String filename,OutputStream os) {
      
              BufferedInputStream bis=null;
              try {
                  bis = new BufferedInputStream(new FileInputStream(filename));
                  byte[] b=new byte[1024];
                  int n=0;
                  while (-1!=(n=bis.read(b))){
                      os.write(b,0,n);
                  }
                  os.flush();
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
                  return "504";
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      //注意!一定要先检查bis是否为null,
                      //当文件不存在时,创建bis失败,其为null,
                      //直接执行关闭操作会抛出无法捕获的异常
                      if(bis!=null) {
                          bis.close();
                      }
                      //抛FileNotFoundException后,执行完这一步,
                      //直接跳出main的for循环执行main的finally
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
      
              return "200";
          }
      }
    
  • 专门处理请求的线程:

      public class LogicThread extends Thread {
          Socket socket;
          InputStream is;
          OutputStream os;
      
          public LogicThread(Socket socket) {
              this.socket = socket;
              //启动线程
              start();
          }
      
          @Override
          public void run() {
              byte[] b=new byte[1024];
              try {
                  os=socket.getOutputStream();
                  is=socket.getInputStream();
                  //进行多次数据交换,客户端应该相应地也发出这么多次数据
                  for (int i=0; i<3; i++){
                      //接收数据
                      int n=is.read(b);
                      //处理数据
                      byte[] response=logic(b,0,n);
                      //反馈
                      os.write(response);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  //关闭流和连接
                  close();
              }
      
          }
      
          /**
           * @Author haien
           * @Description 处理数据,实际就是直接拷贝
           * @Date 2019/6/26
           * @Param [b, start, len]
           * @return byte[]
           **/
          public byte[] logic(byte[] b,int start,int len){
              byte[] response=new byte[len];
              //直接拷贝数组
              System.arraycopy(b,0,response,0,len);
              return response;
          }
      
          /**
           * @Author haien
           * @Description 关闭流和连接
           * @Date 2019/6/26
           * @Param []
           * @return void
           **/
          public void close(){
              try {
                  is.close();
                  os.close();
                  socket.close();
              } catch (IOException e) {
      
              }
          }
      }
    
  • 代码示例:ideaProjects/jar-test/ftp

完整版本

  • 代码示例:ideaProjects/ftp-server

你可能感兴趣的:(socket编程实现ftp服务器)