1

首先,我知道这里已经有几篇关于类似问题的帖子,但我已经浏览过它们,他们的问题不是我的问题,他们的解决方案也不能解决我的问题。如果有人有解决我问题的帖子,请回复链接,我会去的。

现在,问题 - 我有一个客户端和一个服务器。客户端请求一个文件,服务器发送它,然后客户端接收它,或者至少它应该是这样的。但事实并非如此。相反会发生什么?客户端接收前 1024 个字节,然后是接下来的 1024 个字节,然后接收 436 个字节(我总是使用同一个文件,所以我总是得到相同的结果)并结束,因为它收到的字节数少于 1024 个字节,因此它有减去它必须执行的最后一次读取,但不应该如此,因为服务器在从其 FileInputStream 读取文件时,读取了更多字节(更多的 1024 字节)。我已经尝试了我想象的一切,但没有得到积极的结果:总是,第三件,而不是被完整接收,

这是我认为的关键代码:

服务器

OutputStream put;
        try {
            ServerSocket serverSocket;
            try {
                serverSocket = new ServerSocket(DesktopClient.PORT_FOR_FILES);
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, ex.toString(), "Error", JOptionPane.ERROR_MESSAGE);
                return false;
            }
            Socket socket;
            try {
                socket = serverSocket.accept();
                ActionWindow.println("Connection for files transference stablished - " + socket);
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(null, ex.toString(), "Error", JOptionPane.ERROR_MESSAGE);
                return false;
            }
            put = socket.getOutputStream();
            BufferedReader requestedVideoBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String requiredVideo = requestedVideoBufferedReader.readLine();
            while (!requiredVideo.matches("finish") && !requiredVideo.matches("errored")) {
                ActionWindow.println("The screen in the router " + ActionWindow.currentConn + " is asking for " + requiredVideo);
                if (new File(ActionWindow.pathToVideosFolder + requiredVideo + ActionWindow.videoFilesExtension).exists()) {
                    try (InputStream in = new FileInputStream(ActionWindow.pathToVideosFolder + requiredVideo + ActionWindow.videoFilesExtension)) {
                        byte[] buf = new byte[1024];
                        int len;
                        System.out.println("About to write");
                        while (true) {
                            len = in.read(buf);
                            System.out.println("len: " + len);
                            if (len > 0) {
                                put.write(buf, 0, len);
                            } else {
                                break;
                            }
                        }
                    }
                    ActionWindow.println("File " + requiredVideo + ActionWindow.videoFilesExtension + " sent.");
                } else {
                    ActionWindow.println("File " + requiredVideo + ActionWindow.videoFilesExtension + " couldn't be found in the local index.");
                    put.close();
                    socket.close();
                    serverSocket.close();
                    return false;
                }
                requiredVideo = requestedVideoBufferedReader.readLine();
            }
            if (requiredVideo.matches("errored")) {
                JOptionPane.showMessageDialog(null, "Se produjo un error en la transmisión de archivos. Los datos del servidor no han sido modificados.", "Error", JOptionPane.ERROR_MESSAGE);
            }
            put.close();
            socket.close();
            serverSocket.close();
            ActionWindow.println("Connecion finished.");
        } catch (IOException ex) {
            if (!(ex instanceof SocketException)) { //This means that the exception has not been caused by finishing the transference.
                JOptionPane.showMessageDialog(null, ex.toString(), "Error", JOptionPane.ERROR_MESSAGE);
                return false;
            } else {
                return true;
            }
        }
        return true;

客户

fileRequester.println(x);
            ServerDaemon.println("Requested " + x);
            File destFile = new File(ServerDaemon.pathToVideosFolder + x + ServerDaemon.videoFilesExtension);
            byte[] buf = new byte[1024];
            OutputStream streamForWritingToFile;
            try {
                streamForWritingToFile = new FileOutputStream(destFile);
            } catch (FileNotFoundException ex) {
                ServerDaemon.println(ex.toString());
                return false;
            }
            int len;
            try {
                while (true) {
                    len = streamFromServer.read(buf);

                    if (len > 0) {
                        streamForWritingToFile.write(buf, 0, len);
                    } else {
                        break;
                    }

                    if (len != 1024) {
                        break;
                    }
                }
            } catch (IOException ex) {
                ServerDaemon.println(ex.toString());
                if (ex instanceof SocketException) {
                    ServerDaemon.disconnect();
                }
                return false;
            }

            ServerDaemon.println("Received file " + x);

编辑:作为一种临时解决方案,我通过 UDP 发送文件大小,以便接收者知道它必须等待多少数据。但是,最好让它通过 TCP 工作,但它不起作用(看看@PeterLawrey 的回答中的评论)。这是我为了使其通过 TCP 工作而尝试过的:

接收者:

BufferedReader inFromClient;
int fileSize;
String receivedSizeMsg = null;
try {
      inFromClient = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      receivedSizeMsg = inFromClient.readLine();
    } catch (IOException ex) {
      ServerDaemon.println(ex.toString());
    }
fileSize = Integer.parseInt(receivedSizeMsg.substring(4));

发件人:

DataOutputStream outToServer;
outToServer = new DataOutputStream(socket.getOutputStream());
outToServer.writeBytes("size" + new File(file+"\n");

第二次编辑:使用 TCP:

接收者:

DataInputStream inFromClient;
int fileSize = 0;
inFromClient = new DataInputStream(socket.getInputStream());
fileSize = Integer.parseInt(inFromClient.readUTF().substring(4));

发件人:

DataOutputStream outToServer;
outToServer = new DataOutputStream(socket.getOutputStream());
outToServer.writeUTF("size" + new File(ActionWindow.pathToVideosFolder + requiredVideo + ActionWindow.videoFilesExtension).length());
4

2 回答 2

3

你这里有问题。

                if (len != 1024) {
                    break;
                }

这假设数据将以与发送时相同的长度到达,这是不正确的。阻塞的 read() 只保证读取一个字节。把它从你的代码中拿出来,它现在就可以工作了。

例如,TCP 通常将数据组合成大约 1500 字节的数据包。当您读取数据时,您可以比发送数据更快地读取它,因此您可能希望读取一个 1024 字节的块,然后是 476 字节(减去任何 TCP 标头)


编辑:一个简单的发送文件和接收文件方法。

public static void sendFile(DataOutputStream out, String fileName) throws IOException {
    FileInputStream fis = new FileInputStream(fileName);
    try {
        byte[] bytes = new byte[8192];
        int len;
        out.writeInt((int) fis.getChannel().size());
        while ((len = fis.read(bytes)) > 0)
            out.write(bytes, 0, len);
    } finally {
        fis.close();
    }
}

public static void recvFile(DataInputStream in, String fileName) throws IOException {
    FileOutputStream fos = new FileOutputStream(fileName);
    try {
        byte[] bytes = new byte[8192];
        int left = in.readInt();
        int len;
        while(left > 0 && (len = in.read(bytes, 0, Math.min(left, bytes.length))) > 0) {
            fos.write(bytes, 0, len);
            left -= len;
        }
    } finally {
        fos.close();
    }
}
于 2012-08-02T12:57:47.937 回答
0

其他人已经给了你答案,基本上,你不能保证在单个数据包中获取数据。在网络中,配置(tcp_nodelay)、数据包大小等变量太多了,最好还是做防御性编程。

建立一个小协议。发送一个 15 字节的标题,其中包含文件的大小,可能还有校验和。任何连接的客户端都必须发送那个 15 字节的标头。然后你的服务器可以坐在一个循环中等待数据。如果您在 N 秒内没有获得更多数据,您甚至可以轮询套接字(选择)并超时。

最后,可以使用校验和来查看文件是否传输成功。

Java World有一篇关于如何在不阻塞的情况下检查套接字的文章,但如果您只是做家庭作业或其他小事,那可能不值得。

于 2012-08-02T13:42:50.293 回答