客户端通过TCP/IP从服务端获取想要的文件
通过TCP/IP协议传输文件比较安全,传输过程中不会丢失文件数据,所以传输文件我们不用UDP协议。
(让我们来分析一下,这个程序使用TCP/IP协议如何传输所需文件。
如图所示,左边为客户端,右边为服务端。
一、服务端要将文件列表发给客户端,由于文件传输不能直接传输内容,只能通过将文件对象序列化之后,传递文件列表信息。此时客户端要接受被序列化的文件后,将其反序列化,得到文件列表信息。传输过程中,我们给文件编号,用map传输文件列表数据,其中map的键为所需下载文件序号,map值问文件的名称;
二、根据传来文件的序号,客户选择文件序号,将文件序号传递给服务端,服务端得到map的键之后,根据键获得值(所需文件的名称),准备传输文件。
三、根据文件名称,和所下载的文件夹路径结合创建名字相同的文件,开始传输文件。
注意:服务端一定要用多线程模式,放入客户端,不然一旦某个客户端出现异常,就会导致整个服务器堵塞崩溃。
传输示图:
具体实现步骤:
服务端:
import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Scanner;/*** 文件服务器的服务端** @param args* @throws IOException*/public class FileServer extends Thread {private File source;private Socket s;public FileServer(File source, Socket s) {this.source = source;this.s = s;}@Overridepublic void run() {try {/**************1.发送文件列表***********/// 获取基于socket对象的输出流ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());//获取文件列表File[] files = source.listFiles();//建map集合,文件序号表示键,文件对象作为值HashMap<String, File> map = new HashMap<>();for (int i = 0; i < files.length; i++) {// 将文件序号(从0开始)map.put(String.valueOf(i), files[i]);}//将map集合发送到客户端oos.writeObject(map);oos.flush();/******2.接受客户端发送的文件序号********/Scanner sc = new Scanner(s.getInputStream());String num = sc.nextLine();System.out.println(\"客户端需要下载的文件编号:\"+num);/**********3.文件传输**********/File file =map.get(num);//获取文件的输出流FileInputStream fis = new FileInputStream(file);BufferedInputStream bis =new BufferedInputStream(fis);//获取基于socket的输出流BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());//传输TransferUtils.transfer(bis, bos);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void main(String[] args) throws IOException {// 准备需要对外提供的文件资源File source = new File(\"F:\\\\英雄时刻\\\\38123882\");// 占据指定串口 创建服务器(1024-49151)ServerSocket ss = new ServerSocket(8888);System.out.println(\"服务器已开启,等待连接\");// 开启循环监听while (true) {// 监听并获取其中一个客户端通信的Socket对象Socket s = ss.accept();System.out.println(\"客户端连接:\"+s.getInetAddress().getHostAddress());// 创建并且启动线程new FileServer(source, s).start();}}}
客户端:
package com.softeem.lesson39;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.ObjectInputStream;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;import java.net.UnknownHostException;import java.util.HashMap;import java.util.Scanner;import javax.swing.filechooser.FileSystemView;public class FileClient {private String fileName;public String getFileName() {return fileName;}/*** 发送文件序号* @throws IOException*/public void sendNum(HashMap<String,File> map,Socket s ) throws IOException{System.out.println(\"请输入需要下载的文件序号:\");Scanner sc = new Scanner(System.in);String num =sc.nextLine();//如果输入的序号没有包含在map的键集中,则递归调用,重新输入序号if(!map.containsKey(num)) {System.out.println(\"没有该序号对应的文件!\");sendNum(map,s);return;}//获取文件名称 给全局变量fileName = map.get(num).getName();//基于socket的输出流PrintWriter pw =new PrintWriter(s.getOutputStream());pw.println(num);pw.flush();}/*** 客户端* @param args* @throws IOException* @throws UnknownHostException* @throws ClassNotFoundException*/public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException {FileClient fc =new FileClient();//建立连接Socket s = new Socket(\"192.168.7.141\",8888);//接受文件列表ObjectInputStream ois =new ObjectInputStream(s.getInputStream());Object obj =ois.readObject();//将对象转换为Map集合HashMap<String,File> map=(HashMap<String,File>)obj;//对map遍历map.forEach((k,v)->{System.out.println(k+\".\"+v.getName());});/*******2.发送需要下载的文件序号********/fc.sendNum(map, s);/********3.接受文件**********/String fname = fc.getFileName();System.out.println(\"准备下载:\"+fname);//获取与本机有关文件系统预览FileSystemView fsv =FileSystemView.getFileSystemView();//获取桌面目录File desketop = fsv.getHomeDirectory();//根据获取的桌面目录以及文件名组合一个新的file对象File target = new File(desketop,fname);//获取文件的输出流OutputStream os =new FileOutputStream(target);//获取网络中的输出流InputStream is = s.getInputStream();//文件传输TransferUtils.transfer(is, os);System.out.println(\"下载完成!\");}}
输入流向输出流传输工具代码:
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;/*** 传输工具类* @author Keerloo**/public class TransferUtils {/*** 将输入流的数据通过输出流输出* @param in* @param out* @throws IOException*/public static void transfer(InputStream in ,OutputStream out) throws IOException {try{byte[] b = new byte[1024];int len = 0;while((len=in.read(b))!=-1) {out.write(b, 0, len);}}finally {if(out!=null)out.close();if(in!=null)in.close();}}}
文件信息: