Trans File 使用 java socket、ObjectInputStream 和 ObjectOutputStream 但被阻塞

问题描述 投票:0回答:1

使用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);
            }
        }
    }
}

结果是: enter image description here 客户端已经转过来了,但是服务器是这样的: enter image description here 它无法继续下去。如何修复它

我尝试过使用DataInputStream和DataOutputStream,但结果是一样的。

java sockets tcp objectinputstream
1个回答
0
投票

根据

read
ObjectInputStream
方法的文档:

此方法将阻塞,直到有一些输入可用为止。

所以基本上你永远不会在服务器端得到

len
等于
-1
。这是因为事件的顺序:

  1. 客户端发送模型,然后发送文件字节。然后客户端等待从服务器接收模型。
    ObjectInputStream
    正在阻塞 I/O,因此以下客户端代码片段中的
    readObject
    也是如此:
    System.out.println("file send over");
    FileTransModel model = (FileTransModel) ois.readObject(); // Blocks here.
    
  2. 服务器接收模型。然后服务器接收正大小的缓冲区,直到另一端关闭。
  3. 但是客户正在等待收到模型,所以不会关闭。
  4. 由于服务器在循环内等待阻塞更多字节或
    -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
将不再可用)。
    

© www.soinside.com 2019 - 2024. All rights reserved.