专注Java教育14年 全国咨询/投诉热线:444-1124-454
赢咖4LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 用Java套接字进行文件传输

用Java套接字进行文件传输

更新时间:2022-11-22 09:03:03 来源:赢咖4 浏览873次

套接字是一种软件(逻辑)端点,它在服务器和一个或多个客户端程序之间建立双向通信。套接字绑定到端口号,以便 TCP 层可以识别数据要发送到的应用程序。应用程序软件定义了一个套接字,以便它利用底层计算机中的端口来实现它。这使程序员能够在其应用程序代码中轻松处理网络通信的低级细节,例如端口、路由等。

套接字如何工作?

客户端和服务器之间的 TCP 套接字通信由几个阶段组成。

Java实现TCP套接字通信

在开始研究文件传输之前,让我们看看如何在 Java 中实现套接字通信。在这里,我们将编写两个 Java 程序。一个是在服务器上运行的程序,另一个是将与服务器通信的客户端程序。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
    private static DataOutputStream dataOutputStream = null;
    private static DataInputStream dataInputStream = null;
    public static void main(String[] args) {
        try(ServerSocket serverSocket = new ServerSocket(5000)){
            System.out.println("listening to port:5000");
            Socket clientSocket = serverSocket.accept();
            System.out.println(clientSocket+" connected\n");
            dataInputStream = new DataInputStream(clientSocket.getInputStream());
            dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
            String message;
            while (true) {
                message = dataInputStream.readUTF();
                System.out.println(message);
                if(message.equalsIgnoreCase("exit()"))
                    break;
            }
            clientSocket.close();
        } catch (Exception e){
            System.out.println(e.toString());
        }
    }
}

在这里,ServerSocket 在本地机器的 5000 端口打开一个套接字并等待客户端,server.accept()接受传入的客户端连接。

连接客户端后,将实例化输出和输入流,可用于使用流提供的方法之一与连接的客户端进行read通信write。

在上面的程序中,服务器循环并接受来自客户端的字符串输入,dataInputStream.readUTF()然后将其打印在控制台上,并在接收到的输入为exit().

import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
    private static DataOutputStream dataOutputStream = null;
    private static DataInputStream dataInputStream = null;
    private static Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) {
        try(Socket socket = new Socket("localhost",5000)){
            dataInputStream = new DataInputStream(socket.getInputStream());
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            while (true) {
                System.out.print("input> ");
                String message = scanner.nextLine();
                dataOutputStream.writeUTF(message);
                if(message.equalsIgnoreCase("exit()"))
                    break;
            }
        }catch (Exception e){
            System.out.println(e.toString());
        }
    }
}

在这里,Socket 将客户端应用程序连接到在 localhost:5000 侦听的上述服务器,在连接被接受后输出和输入流被实例化。

该程序接受来自控制台的字符串输入,然后使用 将其发送到服务器,dataOutputStream.writeUTF()并在扫描的输入匹配时退出循环exit()。

运行程序:

首先,运行Server.java程序,然后运行Client.java程序。您将在服务器端看到连接已接受消息,客户端将接受输入。客户端键入的输入将在服务器端回显,以exit()在打破while(true)循环的客户端应用程序中退出类型。

如果你做到了这一步,你就已经获得了足够的知识来完成任务。

有什么问题?

文件传输会像这样简单:

void sendFile(String filePath){ 
    byte[] buffer = new byte[Integer.MAX_VALUE]; 
    fileInputStream = new FileInputStream(文件路径); 
    int bytes = fileInputStream.read(buffer,0,buffer.length); 
    dataOutputStream.write(buffer,0,bytes); 
}
void receiveFile(String newFileName){ 
    byte[] buffer = new byte[Integer.MAX_VALUE]; 
    int bytes = dataInputStream.read(buffer,0,buffer.length); 
    fileOutputStream = new FileOutputStream(newFileName); 
    fileOutputStream.write(buffer,0,bytes); 
}

但是这段代码有两个主要问题。

文件大小:

有人可能会说,如果文件大小大于Integer.MAX_VALUE字节,即 2GB,如果是这种情况,这就足够了。

真正的问题是套接字一次只允许 65482 字节,即 64KB,这比上传到上的大多数文档文件大小要小得多网。

多个文件:

即使文件小于 64KB,此代码的另一个问题是在发送多个文件并检测 EOF 时,发送方不会有任何问题,因为fileInputStream.read()它隐式地用于打开的文件(因为 EOF 导致流结束本身)。

但是在从多个文件接收数据的接收端,dataInputStream一个接一个地排队,并且没有任何方法可以分别检测每个文件的 EOF,因此,它将读取排队的整个字节作为一个文件。fileOutputStream.write(buffer)当检测到第一个文件的 EOF 并且剩余文件丢失时,使用写入停止写入此字节缓冲区。

因此,上述代码仅适用于大小小于 64KB 的单个文件。

解决方案:

文件大小:这个问题的一个明显解决方案是将文件分成多个块,比如 4KB,然后在循环语句中通过套接字发送它们,并以类似的方式接收,即wile(stream.read(buffer)!=-1){ ... }

多个文件:这个问题的主要原因是我们不知道接收到的字节中每个文件的 EOF dataInputStream,这可以通过在发送每个文件之前发送文件大小(以字节为单位)来解决,因此我们可以从dataInputStream之后中断读取文件大小并对字节缓冲区中排队的剩余文件重复相同的操作。

修改代码:

import java.io.*;
import java.net.Socket;
public class Client {
    private static DataOutputStream dataOutputStream = null;
    private static DataInputStream dataInputStream = null;
    public static void main(String[] args) {
        try(Socket socket = new Socket("localhost",5000)) {
            dataInputStream = new DataInputStream(socket.getInputStream());
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            sendFile("path/to/file1.pdf");
            sendFile("path/to/file2.pdf");           
            dataInputStream.close();
            dataInputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    private static void sendFile(String path) throws Exception{
        int bytes = 0;
        File file = new File(path);
        FileInputStream fileInputStream = new FileInputStream(file);        
        // send file size
        dataOutputStream.writeLong(file.length());  
        // break file into chunks
        byte[] buffer = new byte[4*1024];
        while ((bytes=fileInputStream.read(buffer))!=-1){
            dataOutputStream.write(buffer,0,bytes);
            dataOutputStream.flush();
        }
        fileInputStream.close();
    }
}

文件发送者

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
    private static DataOutputStream dataOutputStream = null;
    private static DataInputStream dataInputStream = null;
    public static void main(String[] args) {
        try(ServerSocket serverSocket = new ServerSocket(5000)){
            System.out.println("listening to port:5000");
            Socket clientSocket = serverSocket.accept();
            System.out.println(clientSocket+" connected.");
            dataInputStream = new DataInputStream(clientSocket.getInputStream());
            dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
            receiveFile("NewFile1.pdf");
            receiveFile("NewFile2.pdf");
            dataInputStream.close();
            dataOutputStream.close();
            clientSocket.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    private static void receiveFile(String fileName) throws Exception{
        int bytes = 0;
        FileOutputStream fileOutputStream = new FileOutputStream(fileName);        
        long size = dataInputStream.readLong();     // read file size
        byte[] buffer = new byte[4*1024];
        while (size > 0 && (bytes = dataInputStream.read(buffer, 0, (int)Math.min(buffer.length, size))) != -1) {
            fileOutputStream.write(buffer,0,bytes);
            size -= bytes;      // read upto file size
        }
        fileOutputStream.close();
    }
}

文件接收器

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>