1

まず第一に、同様の問題に関するいくつかの投稿が既にここにあることを知っていますが、私はそれらを調べましたが、それらの問題は私の問題ではなく、それらの解決策は私の問題を解決します. 誰かが私の質問を解決する投稿を持っている場合は、リンクを返信してください。

さて、問題はクライアントとサーバーです。クライアントがファイルを要求し、サーバーがそれを送信し、クライアントがそれを受信するか、少なくともそうあるべきです。そうではありません。代わりに何が起こりますか?クライアントは最初の 1024 バイトを受信し、次に次の 1024 バイトを受信し、次に 436 バイトを受信し (私は常に同じファイルを使用しているため、常に同じ結果が得られます)、受信したのが 1024 バイト未満であるため終了します。実行する必要があった最後の読み取りよりも差し引かれますが、サーバーが FileInputStream からファイルを読み取るときに、はるかに多くのバイト (1024 バイトのはるかに多くの部分) を読み取るため、そうすべきではありません。思いつく限りのことをしてみたのに、一向に好結果が出ないいつも、3枚目は、まるごと受け取られるどころか、

そして、これが私がキーコードだと思うものです:

サーバ

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");

2番目の編集: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() は、1 バイトの読み取りのみが保証されます。これをコードから取り出すと、すぐに機能する可能性があります。

たとえば、TCP は多くの場合、データを約 1500 バイトのパケットに結合します。データを読み取るときは、送信されるよりも速く読み取ることができるため、1024 バイトのブロックに続いて 476 バイト (TCP ヘッダーを除く) を読み取ると予想される場合があります。


EDIT:単純なファイルの送信とファイルの受信方法。

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 に答える