socket传输文件完成后,文件打不开,小文件可以,但似乎分块传输就不行,是初步怀疑黏包问题?希望排查修正一下
clent:
public class Client {
public static void main(String[] args) {
File file=new File("D:\\data.mp4");
try (
Socket socket = new Socket("127.0.0.1", 12345);
FileInputStream fileInputStream=new FileInputStream(file);
ObjectOutputStream objectOutputStream=new ObjectOutputStream(socket.getOutputStream());
) {
byte[] buffer = new byte[1024000];
int blockSize = 1024000; // 块大小
//向上取整
int totalBlocks = (int) Math.ceil((double) file.length() / blockSize);
long bytesRead,chunkNumber=1;
while((bytesRead=fileInputStream.read(buffer))!=-1){
FileInfo fileInfo=new FileInfo(file.getName(),totalBlocks, Math.toIntExact(chunkNumber),bytesRead,buffer);
objectOutputStream.writeObject(fileInfo);
objectOutputStream.flush();
chunkNumber++;
}
// //往服务器写出结束标记
// socket.shutdownOutput();
System.out.println("传输完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileInfo :
@Data
public class FileInfo implements Serializable {
private String fileName;
private int totalBlocks;//分块总数
private Integer chunkNumber;//当前分块序号
private long currentChunkSize;//当前分块大小
private byte[] data;
public FileInfo(String fileName, int totalBlocks, Integer chunkNumber, Long currentChunkSize, byte[] data) {
this.fileName = fileName;
this.totalBlocks = totalBlocks;
this.chunkNumber = chunkNumber;
this.currentChunkSize = currentChunkSize;
this.data = data;
}
}
Server :
public class Server {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(12345)) {
while (true){
Socket socket = serverSocket.accept();
ServerThread serverThread=new ServerThread(socket);
Thread thread=new Thread(serverThread);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
ServerThread :
public class ServerThread implements Runnable{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (
ObjectInputStream objectInputStream=new ObjectInputStream(socket.getInputStream());
) {
while (true){
FileInfo fileInfo=(FileInfo)objectInputStream.readObject();
String fileName = fileInfo.getFileName();
long fileSize = fileInfo.getCurrentChunkSize();
System.out.println("正在接收文件: " + fileName + " (" + fileSize + " bytes)");
FileOutputStream fos = new FileOutputStream(fileName,true);
fos.write(fileInfo.getData(),0,(int) fileInfo.getCurrentChunkSize());
fos.flush();
fos.close(); // 关闭 FileOutputStream 对象
if(fileInfo.getChunkNumber()==fileInfo.getTotalBlocks()){
System.out.println("文件传输完成!");
break;
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
【以下回答由 GPT 生成】
根据提供的信息,问题出现在使用Java的Socket传输文件时,传输完成后无法打开接收到的文件。小文件可以正常传输,但是对于大文件来说,似乎分块传输就出错了,产生的猜测是黏包问题导致的。
对于黏包问题,一般发生在数据的发送和接收过程中,并且是因为一次接收到的数据大小超过了期望的大小,导致多个数据包黏在一起,造成解析错误。
在这个问题中,我们可以猜测黏包问题是由于数据不完整或者传输不及时导致的。当传输大文件时,我们需要分块读取文件内容并发送,但是每次发送数据时,都需要等待接收方确认接收完毕,否则可能会导致多次发送数据,从而发生黏包问题。
为了解决这个问题,我们需要对代码进行一些调整和优化。
首先,我们需要在发送数据之后等待接收方的确认。可以在发送每个数据块之后,都加入一个确认消息的处理。
然后,为了保证每个块的信息都能够正确地传输,我们需要将文件块的具体内容也加入到FileInfo
对象中进行传输。
最后,接收方收到每个块的时候需要进行处理,将接收到的数据写入到文件中。
下面是更新后的代码:
public class Client {
public static void main(String[] args) {
File file = new File("D:\\data.mp4");
try (
Socket socket = new Socket("127.0.0.1", 12345);
FileInputStream fileInputStream = new FileInputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
) {
byte[] buffer = new byte[1024000];
int blockSize = 1024000; // 块大小
int totalBlocks = (int) Math.ceil((double) file.length() / blockSize); // 分块总数
long bytesRead, chunkNumber = 1;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
FileInfo fileInfo = new FileInfo(file.getName(), totalBlocks, (int) Math.toIntExact(chunkNumber), bytesRead, buffer);
objectOutputStream.writeObject(fileInfo);
objectOutputStream.flush();
chunkNumber++;
// 等待接收方的确认
String confirmation = (String) objectInputStream.readObject();
if (!confirmation.equals("OK")) {
System.out.println("接收方确认错误!");
break;
}
}
System.out.println("传输完成!");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
接下来,我们需要调整服务器端的代码以接收和写入文件数据。
public class Server {
public static void main(String[] args) {
try (
ServerSocket serverSocket = new ServerSocket(12345);
Socket socket = serverSocket.accept();
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
) {
FileInfo fileInfo;
while ((fileInfo = (FileInfo) objectInputStream.readObject()) != null) {
FileOutputStream fileOutputStream = new FileOutputStream("D:\\received_" + fileInfo.getFileName(), true);
fileOutputStream.write(fileInfo.getData(), 0, fileInfo.getSize());
// 发送确认消息
objectOutputStream.writeObject("OK");
objectOutputStream.flush();
System.out.println("块 " + fileInfo.getChunkNumber() + " 接收并写入完成!");
if (fileInfo.getChunkNumber() == fileInfo.getTotalBlocks()) {
// 所有块已经接收完毕
break;
}
}
System.out.println("文件接收完毕!");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
更新后的代码在每次发送一个块之后,等待接收方的确认消息,确认成功后才继续发送下一个块。
接收方接收到每个块之后,将其写入到指定的文件中,并发送确认消息。
这样,通过加入确认机制,我们可以尽可能地避免黏包问题的发生,从而保证文件能够成功传输并且正确打开。
请尝试使用更新后的代码,如果还有其他问题,或者对解决方案有疑问,请随时告诉我。