5

Javaで実行されている「サーバー」があり、バイト配列を介して「クライアント」(Androidシステム)にファイルを送信しています。機能は正常に動作しますが、より大きなファイル (1MB 以上) を送信しようとすると、閉じる前にすべてのバイトが正常に書き込まれないことに気付きました (例: 5MB を送信しようとして、902 バイトしか送信されませんでした)。ストリームを閉じる前に、ストリームの書き込みが完了するまでコードを待機させる方法はありますか? 以下は、コードとスタック トレースです。基本的に、メッセージの送信メソッドは、ファイルが添付されて送信されるときに呼び出されます (GUI を処理するクラスで)。この場合、クライアントからデータを受信しようとしているのではなく、クライアントにデータを送信するだけです。

編集:現在、バイト配列を1MBにしか設定していないことを認識しています。私はそれを5MBで持っていましたが、まだ1MB未満しか書いていませんでした。1MB でも、まだ 902 バイトしか書き込みませんでした。

EDIT2: データの受信を処理する CLIENT 側のコードの一部を投稿しました。

public void sendMessage(File file) {
    if(mOut != null) {
        try {
            FileInputStream inStream = new FileInputStream(file);               
            byte[] message = new byte[1024 * 1024];
            inStream.read(message);
            mOut.write(message, 0, message.length);
            mOut.flush();
            inStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

public void run() { 
    super.run();
    running = true;
    try {
        System.out.println("S: Connecting...");
        ServerSocket serverSocket = new ServerSocket(SERVERPORT);
        Socket client = serverSocket.accept();
        linked = true;
        System.out.println("S: Receiving...");

        try {
            mOut =client.getOutputStream();
            InputStream in = client.getInputStream();
            while(running) {
                byte[] message = new byte[1024 * 1024];
                 in.read(message);
                if(message != null && messageListener != null) {
                    messageListener.messageReceived(message);
                }
            }
        } catch (Exception ex) {
            System.out.println("S:Error");
            ex.printStackTrace();
        } finally {
            client.close();
            System.out.println("S:Done");
        }
    } catch (Exception ex) {
        System.out.println("S: Error");
        ex.printStackTrace();
    }

}
---------------------------------------------------------------------------
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at Server.sendMessage(Server.java:34)
at ServerBoard$2.actionPerformed(ServerBoard.java:57)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at Server.run(Server.java:58)

クライアントのコード スニペットを次に示します。

    public void run() {
    mRun = true;
    serverMessage = new byte[1024 * 1024];
    try {
        InetAddress serverAddr = InetAddress.getByName(SERVERIP);
        Log.e("TCP Client", "C:Connecting...");
        Socket socket = new Socket(serverAddr, SERVERPORT);
        try {
            in = socket.getInputStream();
            while(mRun) {
                 in.read(serverMessage);
                if(serverMessage != null && mMessageListener != null) {
                    mMessageListener.messageReceived(new String(serverMessage));
            }
                serverMessage = null;
            }

            Log.e("RESPONSE FROM SERVER","S:Received Message: '" + serverMessage + "'");

        } catch(Exception ex) {
            Log.e("TCP","S: Error", ex);
        } finally {
            socket.close();
        }
    } catch(Exception ex) {
        Log.e("TCP","C: Error", ex);
    }
}
4

3 に答える 3

3

まず、readの JavaDoc :

入力ストリームからいくつかのバイトを読み取り、それらをバッファ配列 b に格納します。実際に読み取られたバイト数は整数として返されます。このメソッドは、入力データが使用可能になるか、ファイルの終わりが検出されるか、例外がスローされるまでブロックされます。

ソースがソケットであるため、バッファーのサイズが 1 MB であっても、902 KB を返すことは問題ありません。ストリームの終わりまでループする必要があります。

次のメソッドは、リスナーを呼び出すよりも、すべてのバイトを読み取ります。

byte[] message = new byte[0]; 
byte[] buffer  = new byte[4096];
int    len;
while(( len = is.read( buffer )) > -1 ) {
   byte[] tmp = new byte[message.length+len];
   System.arraycopy( message, 0, tmp, 0, message.length );
   System.arraycopy( buffer , 0, tmp, message.length, len );
   message = tmp;
}
messageListener.messageReceived( message );

ご覧のとおり、多くの再割り当てが発生します。最初にファイルのサイズを送信し、一度受信バッファーを割り当てるようにプロトコルを再設計する必要があります。

于 2013-04-01T14:48:20.863 に答える