Java通过套接字传输多个文件
Posted
技术标签:
【中文标题】Java通过套接字传输多个文件【英文标题】:Java multiple file transfer over socket 【发布时间】:2020-12-02 08:14:19 【问题描述】:好的,尝试通过套接字传输指定的文件目录,从数组列表中删除目录对象,因此只剩下文件,然后通过同一个套接字将它们一一传输。这里的arraylist 只填充了文件,没有目录。下面分别是客户端和服务器的接收和发送代码。代码运行良好,没有错误,除了所有数据都被写入第一个文件。后续文件在服务器文件夹中创建,但它们是 0 字节。任何意见将不胜感激。
这是接收文件的服务器代码
public void receive()
try
DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
//read the number of files from the client
int number = dis.readInt();
ArrayList<File>files = new ArrayList<File>(number);
System.out.println("Number of Files to be received: " +number);
//read file names, add files to arraylist
for(int i = 0; i< number;i++)
File file = new File(dis.readUTF());
files.add(file);
int n = 0;
byte[]buf = new byte[4092];
//outer loop, executes one for each file
for(int i = 0; i < files.size();i++)
System.out.println("Receiving file: " + files.get(i).getName());
//create a new fileoutputstream for each new file
FileOutputStream fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" +files.get(i).getName());
//read file
while((n = dis.read(buf)) != -1)
fos.write(buf,0,n);
fos.flush();
fos.close();
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
这是发送文件的客户端代码
public void send(ArrayList<File>files)
try
DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
System.out.println(files.size());
//write the number of files to the server
dos.writeInt(files.size());
dos.flush();
//write file names
for(int i = 0 ; i < files.size();i++)
dos.writeUTF(files.get(i).getName());
dos.flush();
//buffer for file writing, to declare inside or outside loop?
int n = 0;
byte[]buf = new byte[4092];
//outer loop, executes one for each file
for(int i =0; i < files.size(); i++)
System.out.println(files.get(i).getName());
//create new fileinputstream for each file
FileInputStream fis = new FileInputStream(files.get(i));
//write file to dos
while((n =fis.read(buf)) != -1)
dos.write(buf,0,n);
dos.flush();
//should i close the dataoutputstream here and make a new one each time?
//or is this good?
dos.close();
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
【问题讨论】:
想自学Java,第一次做一些简单的项目玩io。我有一种感觉,我的 dos.write(buf,0,n) 及其接收伙伴只是继续读写,因此所有数据都被写入第一个文件?当第一个文件完全写入时,我是否需要添加一个控件来发出信号? 嗯,还是一样的问题,所有数据都写入第一个文件,不知道我做错了什么。 另一种选择是在“标题”中写入每个文件的大小以及文件名。然后您的服务器端代码可以知道何时停止写入每个文件,并开始下一个。 【参考方案1】:您正在读取套接字,直到 read()
返回 -1。这是流结束条件 (EOS)。 EOS 在对等方关闭连接时发生。当它完成一个文件的写入时不是。
您需要在每个文件之前发送文件大小。您已经对文件计数做了类似的事情。然后确保您读取了该文件的确切字节数:
String filename = dis.readUTF();
long fileSize = dis.readLong();
FileOutputStream fos = new FileOutputStream(filename);
while (fileSize > 0 && (n = dis.read(buf, 0, (int)Math.min(buf.length, fileSize))) != -1)
fos.write(buf,0,n);
fileSize -= n;
fos.close();
您可以将所有这些包含在一个循环中,该循环在 readUTF()
抛出 EOFException
时终止。相反,当然您必须在发送数据之前调用发送方的writeUTF(filename)
和writeLong(filesize)
。
【讨论】:
shutdownOutput
不能也用来表示EOF
吗?
@user384706 是的。每个连接一次。所以你只能发送一个文件。
效果很好,我对 n = dis.read(buf,0,Math.min) 部分感到困惑,math.min 是否确定文件大小是否小于缓冲区大小?如果是这样,它只会用 file.length 部分填充缓冲区?
@Tom Math.min()
是为了正确处理最后一个缓冲区。您不能假设文件大小是缓冲区长度的倍数。
@FadlyDzil 这不是编码服务。我已经提供了所有必要的代码。我希望任何有能力的程序员都能将我的其他评论变成工作代码。【参考方案2】:
我是这样弄的,效果很好,你可以看看:
发送
byte[] done = new byte[3];
String str = "done"; //randomly anything
done = str.getBytes();
for (int i = 0; i < files.size(); i++)
System.out.println(files.get(i).getName());
FileInputStream fis = new FileInputStream(files.get(i));
while ((n = fis.read(buf)) != -1)
dos.write(buf, 0, n);
System.out.println(n);
dos.flush();
//should i close the dataoutputstream here and make a new one each time?
dos.write(done, 0, 3);
dos.flush();
//or is this good?
dos.close();
接收
for (int i = 0; i < files.size(); i++)
System.out.println("Receiving file: " + files.get(i).getName());
//create a new fileoutputstream for each new file
fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" + files.get(i).getName());
//read file
while ((n = dis.read(buf)) != -1 && n != 3)
fos.write(buf, 0, n);
fos.flush();
fos.close();
【讨论】:
感谢您的问题,我了解到您要求在每次文件传输后关闭DataOutputStream
的新内容,它实际上关闭了底层流,因此套接字有点关闭。 Close()-Closes this output stream and releases any system resources associated with the stream.
Javadocs。
嗯,我现在正在发送文件大小并将它们存储在一个 int 数组中。我将此添加到我的服务器 while 循环中,while((n = dis.read(buf)) != -1 && bytesRead
好的,我也试过了,但是有几个字节的丢失/增益。只需检查最后传输的文件,它是否丢失了几个字节?
哦耶,比如接收5个文件,有时前3次迭代会接收所有字节,最后2个文件是0字节,有时会接收到第4次迭代的字节.很随意
虽然,请阅读上面将 Math.min 添加到 while((n = dis.read(buf,0,Math.min(n, FileSizes[i])) 的帖子。那更正了问题完全,但在这个过程中扰乱了我的大脑【参考方案3】:
我已经创建了一个服务器和客户端。他们正在建立连接,之后服务器每秒发送 1MB 的文本文件。服务器和客户端代码如下。我测试了很长时间,发现没有数据丢失。上面回答的我稍微修改了一下。
服务器代码:
package com.dd.server;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
public class Server
public static void main(String[] args) throws InterruptedException
try
byte[] done = new byte[3];
String str = "done"; //randomly anything
done = str.getBytes();
ServerSocket ss = new ServerSocket(5000);
Socket socket = ss.accept();
byte[] mybytearray = new byte[4096];
OutputStream os = socket.getOutputStream();
TimeUnit.SECONDS.sleep(5);
while(true)
DataOutputStream dos = new DataOutputStream(os);
File myFile= new File("I:\\MY-LEARNINGS\\JAVA\\Workspace\\server\\src\\com\\dd\\server\\gistfile1.txt");
dos.writeUTF(myFile.getName());
dos.writeLong(myFile.length());
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
int read;
System.out.println("---------File Writing started----------");
int count = 0;
while((read = dis.read(mybytearray)) != -1)
dos.write(mybytearray, 0, read);
dos.flush();
++count;
System.out.println("Writing sub component of file. Step : "+count);
System.out.println("---------File Writing ended----------");
System.out.println("Flushing data DONE command sent.");
dis.close();
bis.close();
fis.close();
TimeUnit.SECONDS.sleep(1);
System.out.println("File transfer has been completed.");
dos.write(done, 0, 3);
dos.flush();
catch (IOException e)
e.printStackTrace();
客户代码:
package clientcom.dd.clent;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client
public static void main(String[] args)
try
Socket socket = new Socket("127.0.0.1", 5000);
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
while(true)
FileOutputStream fos =
new FileOutputStream("I:\\MY-LEARNINGS\\JAVA\\Workspace\\client\\"+System.currentTimeMillis()+"-data.txt");
int read = 0;
byte[] mybytearray = new byte[4096];
while ((read = dis.read(mybytearray)) != -1 && read != 3)
fos.write(mybytearray, 0, read);
fos.flush();
fos.close();
//System.out.println("The value of read : "+read);
// System.out.println("File has been received successfully.");
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
【讨论】:
以上是关于Java通过套接字传输多个文件的主要内容,如果未能解决你的问题,请参考以下文章
Java - 通过套接字发送文件(聊天客户端 -> 服务器 -> 另一个聊天客户端)
在 java /android 中提高 TCP 套接字的传输速度
通过 file.createWriteStream() 流式传输到 GCP 时,获取 ESOCKETTIMEOUT、ECONNRESET 或套接字挂起大文件