使用socket BIO发送文件使用Socket、ServerSocket、ObjectInputStream和ObjectOutputStream但被阻塞。详细代码为: 型号:
@Data
@ToString
public class FileTransModel implements Serializable {
private String fileName;
private Long fileLength;
private Integer status;
}
客户:
public class FileTransClient {
private static final int BUFFER_SIZE = 1024 * 8;
private static final byte[] BUFFER = new byte[BUFFER_SIZE];
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 1888);
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {
File file = new File("D:\\trans\\1.mp4");
FileTransModel fileTransModel = new FileTransModel();
fileTransModel.setFileName(file.getName());
fileTransModel.setFileLength(file.length());
oos.writeObject(fileTransModel);
FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
int len;
while ((len = bufferedInputStream.read(BUFFER)) != -1) {
System.out.println(len);
oos.write(BUFFER, 0, len);
oos.flush();
}
System.out.println("file send over");
FileTransModel model = (FileTransModel) ois.readObject();
System.out.println(model);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
服务器:
public class FileTransServer {
private static final int BUFFER_SIZE = 1024 * 8;
private static final byte[] BUFFER = new byte[BUFFER_SIZE];
public static void main(String[] args) {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(1888);
System.out.println("Socket Server start on port: 1888");
while(true) {
Socket accept = serverSocket.accept();
System.out.println("new file come!");
new Thread(new Task(accept)).start();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static class Task implements Runnable {
Socket socket;
public Task(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
FileTransModel fileTransModel = (FileTransModel) ois.readObject();
System.out.println(fileTransModel);
File file = new File("D:\\trans_rec2\\"+fileTransModel.getFileName());
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fos);
int len;
len = ois.read(BUFFER);
int size = 0;
while (true) {
size += len;
System.out.println(len + ": " + size);
if (len == -1) {
break;
}
bufferedOutputStream.write(BUFFER, 0, len);
bufferedOutputStream.flush();
len = ois.read(BUFFER);
}
System.out.println("file write over");
fileTransModel.setStatus(0);
oos.writeObject(fileTransModel);
System.out.println("file receive over");
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
结果是: 客户端已经转过来了,但是服务器是这样的: 它无法继续下去。如何修复它
我尝试过使用DataInputStream和DataOutputStream,但结果是一样的。
根据
read
中ObjectInputStream
方法的文档:
此方法将阻塞,直到有一些输入可用为止。
所以基本上你永远不会在服务器端得到
len
等于-1
。这是因为事件的顺序:
ObjectInputStream
正在阻塞 I/O,因此以下客户端代码片段中的 readObject
也是如此:
System.out.println("file send over");
FileTransModel model = (FileTransModel) ois.readObject(); // Blocks here.
-1
(假设没有出现IOException
),那么它不会将模型发送回。结果双方都被阻塞等待。
为了解决这个问题,您可以以某种方式表示文件传输结束,以便服务器知道何时停止读取文件字节。由于文件内容可以是任意的,并且其大小不是在服务器端预先确定的,因此您可以在开始将其内容发送到服务器之前发送文件大小。但模型已经有了这些信息,所以你在服务器端的读取循环可以修改为:
int len;
len = ois.read(BUFFER);
int size = 0;
while (true) {
size += len;
System.out.println(len + ": " + size);
if (len == -1) {
break;
}
bufferedOutputStream.write(BUFFER, 0, len);
bufferedOutputStream.flush();
len = ois.read(BUFFER);
}
至:
try (final FileOutputStream fos = new FileOutputStream(file);
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fos)) {
final long expected = fileTransModel.getFileLength() == null? 0: fileTransModel.getFileLength();
long size = 0;
while (size < expected) {
final int current = ois.read(BUFFER);
bufferedOutputStream.write(BUFFER, 0, current);
//bufferedOutputStream.flush(); //Flushing after each write would defeat the purpose of buffering, so better don't do it.
size += current;
System.out.println("So far: " + size);
}
} //Always close the file.
在写入文件内容后立即关闭客户端
ObjectOutputStream
(
oos
参考),例如为了在服务器端获取 -1
,这里不是一个选项,因为关闭 oos
会关闭OutputStream
的底层 Socket
,根据文档,这又会关闭 Socket
本身。因此,如果您要将模型发送回(即从服务器到客户端),则会引发 SocketException
(Socket
将不再可用)。