在局域网中,客户可以将文件分享到网络上,由服务器进行转发给其他客户,其他客户可以接收服务器发来的文件,并保存到本地磁盘中。
以下是大致的工作流程
客户端有三个线程
主线程 1.负责启动 文件发送的线程 2.负责启动文件接收线程
内容如图:
服务端有两个线程,主线程专门接收用的连接,并为每一个连接上服务器的客户创建一个子线程。子线程专门用来接收客户发来的文件,保存本地磁盘,并转发给其他客户。如下:
客户端主线程代码:
import java.net.Socket; //客户端 public class Client { public static void main(String[] args) { try { System.out.println("=======客户端======="); //1.线连接上服务器的套接字 (套接字=ip:端口号) Socket socket = new Socket("127.0.0.1", 10001); //2. 开启一个线程对象 专们用来接收文件的线程 new ClientGetFile(socket).start(); //3.开启发送文件的线程对象 new ClientSendFile(socket).start(); while (true); } catch (Exception e) { e.printStackTrace(); } } }
客户端接收文件子线程代码:
import java.io.*; import java.net.Socket; //作为客户端接收文件的线程 public class ClientGetFile extends Thread{ private final Socket socket; public ClientGetFile(Socket socket) { this.socket = socket; } @Override public void run() { try { //1.定义 输入流 从网络介质中获取数据存入内存中 DataInputStream dis=new DataInputStream(socket.getInputStream()); //2. 定义流将文件数据从内存中写入磁盘 String FileName = dis.readUTF(); String FilePath ="E:\\Documets\\Desktop\\客户\\"+ FileName; System.out.println("正在保存:"+FileName); DataOutputStream outputToDisk = new DataOutputStream(new FileOutputStream(FilePath)); byte[] buffer=new byte[8192]; long FileLength = dis.readLong(); int length; int OKLength=0; while ((length=dis.read(buffer))!=-1){ OKLength += length; outputToDisk.write(buffer,0, length); outputToDisk.flush(); if (FileLength==OKLength) break; } System.out.println("接收"+FileName+"成功!"); System.out.println("路径为:"+FilePath); outputToDisk.close(); //关闭 内存->磁盘 的io资源 } catch (Exception e) { System.out.println("您已离线!!!"); e.printStackTrace(); } } }
客户端发送文件子线程代码:
import java.io.*; import java.net.Socket; //作为客户端发送文件的线程类 public class ClientSendFile extends Thread{ private final Socket socket; public ClientSendFile(Socket socket) { this.socket = socket; } @Override public void run() { try { //1.创建要发送的文件对象 File file=new File("E:\\Documets\\Desktop\\老婆.mp4"); //2.通过套接字传文件给服务器,由服务器转发给别的客户 sendFileToServer(socket,file); System.out.println(file.getName()+"发送完毕!!!"); //while (true); } catch (Exception e) { System.out.println("您已离线!!"); e.printStackTrace(); } } //定义一个静态方法 作为专门发送文件 private static void sendFileToServer(Socket socket,File file) throws Exception { //1.将文件对象输入到内存中来 DataInputStream InputToRAM=new DataInputStream( new FileInputStream(file) ); //2.准备发送管道 发送到网络 给服务器接收 DataOutputStream outputToNet= new DataOutputStream( socket.getOutputStream() ); //3.发送文件名、文件大小 给服务器 outputToNet.writeUTF(file.getName()); //发送文件名给 服务器 file.getName(); 得到要发送的文件名 outputToNet.flush(); //刷新流 outputToNet.writeLong(file.length()); // 发送文件大小给 服务器 file.length();得到要发送的文件大小 单位字节(1K=1024Byte) outputToNet.flush(); // 4.发送文件内容 给服务器 int length; byte[] buffer = new byte[1024]; while ((length = InputToRAM.read(buffer)) != -1) { //dis.read(buffer) 从磁盘中读取内容,存到buffer数组中(数组在内存中) outputToNet.write(buffer,0,length); outputToNet.flush(); } } }
=========================================================================
服务器主线程代码:
import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; //使用两个线程 主线程 用于将连接过来的套接字 添加到集合 代表当前在线的人数 public class Server { /*创建一个Socket的list集合 用来装套接字 Socket=(IP地址:端口号) */ public static final List<Socket> onLineSockets = new ArrayList<>(); //当客户端连接上服务器的时候,就将客户端的套接字存入集合中 public static void main(String[] args) { try { System.out.println("===服务端启动成功==="); // 1、注册端口 ServerSocket serverSocket = new ServerSocket(10001); // a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。 while (true) { // 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息 Socket socket = serverSocket.accept(); System.out.println(socket.getRemoteSocketAddress()+ "上线了!"); onLineSockets.add(socket);// 把当前客户端管道Socket加入到在线集合中去 // 3、开始创建独立线程处理这个连接上来的客户 new ServerFileThread(socket).start(); } } catch (Exception e) { System.out.println("您已离线!"); e.printStackTrace(); } } }
服务器子线程,负责文件的保存和转发:
import java.io.*; import java.net.Socket; //服务端转发文件给客户线程 public class ServerFileThread extends Thread{ private final Socket socket; //用来存套接字 public ServerFileThread(Socket socket){ //构造方法,用来接收 与服务器连接的管道对管道进行读操作 this.socket = socket; } @Override public void run() { try { //1.得到客户的通信管道 DataInputStream InputToRAM=new DataInputStream(socket.getInputStream()); //2.准备输出流 一个输出到服务器的本地磁盘下,一个输出到网络介质上,让其他客户接收 DataOutputStream outputToDisk; DataOutputStream outputToNet = new DataOutputStream(socket.getOutputStream()); //3. 接收和转发文件 (接收是接收到服务器的磁盘下,转发是转发到 网络介质上给别的客户) while (true) { // 获取文件名字和文件长度 String FileName = InputToRAM.readUTF(); String FilePaht ="E:\\Documets\\Desktop\\服务器\\"+ FileName; System.out.println("正在接收:"+FileName); outputToDisk=new DataOutputStream(new FileOutputStream(FilePaht)); long FileLength = InputToRAM.readLong(); // 发送文件名字和文件长度给所有客户端 for(Socket onLineSocket : Server.onLineSockets) { //onLineSockets存的是当前连接的客户 if(onLineSocket!= socket) { // 发送给其它客户端 outputToNet.writeUTF(FileName); outputToNet.flush(); outputToNet.writeLong(FileLength); outputToNet.flush(); } } //真正传送文件数据 int length; long OKLength = 0; byte[] buffer = new byte[8192]; while ((length = InputToRAM.read(buffer)) != -1) { OKLength += length; //记录已经传输的文件大小 //存到服务器的磁盘下 outputToDisk.write(buffer, 0, length); outputToDisk.flush(); //转发数据到每个用户 for(Socket onLineSocket : Server.onLineSockets) { if(onLineSocket != socket) { // 发送给其它客户端, outputToNet.write(buffer, 0, length); outputToNet.flush(); } } if(OKLength == FileLength) { // 强制退出 break; } } System.out.println(FileName+"转发完毕!"); System.out.println(FileName+"保存到服务器的路径为:"+FilePaht); outputToDisk.close(); //关闭 内存->磁盘 的io资源 } } catch (IOException e) { Server.onLineSockets.remove(socket); System.out.println(socket.getRemoteSocketAddress()+"已下线~"); //该用户已经断开连接 System.out.println("当前在线人数:"+Server.onLineSockets.size()); // e.printStackTrace(); } } }
实验结果:
服务器
客户端:
在E:\Documets\Desktop\服务器\下有 “老婆.mp4” 文件,且可以正常打开
在E:\Documets\Desktop\客户\路径下也有,并可以正常打开
再来说说怎么解决 服务器怎么区别,客户发来的文件 是什么类型的文件,解决的办法是先将问文件名和文件大小发送给服务器,然后让服务器分配好io输出到本地磁盘的路径,之后再进行文件数据的传输,这样就可以了,详情参考上面的代码。