Java模拟文件发送给服务器,服务器将文件转发给其他用户,并保存到服务器本地,其他用户可以接收,并保存到本地磁盘,支持各种文件格式,并解决通信中服务器怎么区分客户端发来的文件类型

简介: Java模拟文件发送给服务器,服务器将文件转发给其他用户,并保存到服务器本地,其他用户可以接收,并保存到本地磁盘,支持各种文件格式,并解决通信中服务器怎么区分客户端发来的文件类型

在局域网中,客户可以将文件分享到网络上,由服务器进行转发给其他客户,其他客户可以接收服务器发来的文件,并保存到本地磁盘中。

以下是大致的工作流程

客户端有三个线程

主线程  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输出到本地磁盘的路径,之后再进行文件数据的传输,这样就可以了,详情参考上面的代码。

相关文章
|
1月前
|
Java
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
69 9
|
8天前
|
存储 人工智能 自然语言处理
ChatMCP:基于 MCP 协议开发的 AI 聊天客户端,支持多语言和自动化安装 MCP 服务器
ChatMCP 是一款基于模型上下文协议(MCP)的 AI 聊天客户端,支持多语言和自动化安装。它能够与多种大型语言模型(LLM)如 OpenAI、Claude 和 OLLama 等进行交互,具备自动化安装 MCP 服务器、SSE 传输支持、自动选择服务器、聊天记录管理等功能。
71 14
ChatMCP:基于 MCP 协议开发的 AI 聊天客户端,支持多语言和自动化安装 MCP 服务器
|
28天前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
38 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
2月前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
41 1
[Java]线程生命周期与线程通信
|
1月前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
38 3
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
24天前
|
Java
Java将OffsetDateTime格式化为 yyyy-MM-dd HH:mm:ss 如何写代码?
Java将OffsetDateTime格式化为 yyyy-MM-dd HH:mm:ss 如何写代码?
27 0
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
24 1
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
49 1
|
2月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
31 1
下一篇
DataWorks