1

かなり複雑な Java サーバー用のアップロード システムを作成しようとしています。以下に示す 2 つの小さなプログラムでエラーを再現しました。基本的に、クライアント/サーバー経由で通信するために ObjectOutputStream/ObjectInputStream を使用しています。これは必須です。この ObjectOutputStream/ObjectInputStream セットアップの周りで何千行ものコードが完全に正常に動作しているため、アップロードが完了した後もこれらのストリームを引き続き使用できる必要があります。

ファイル (クライアントで読み取られるファイルとサーバーで書き込まれるファイル) にアクセスするには、FileInputStream と FileOutputStream が使用されます。私のクライアント完璧に機能しているようです。ファイルを読み取り、反復ごとに異なるバイト配列を送信します (一度に 1MB を読み取るため、ヒープをオーバーフローすることなく大きなファイルを処理できます)。ただし、サーバー上では、バイト配列は常に最初に送信された配列 (ファイルの最初の 1MB) であるかのように見えます。これは、私の ObjectInputStream/ObjectOutputStream の理解と一致しません。私は、この問題に対する実用的な解決策、または自分自身の解決策を形成するための十分な教育を求めています。

以下はクライアントコードです。

import java.net.*;
import java.io.*;

public class stupidClient
{
  public static void main(String[] args)
  {
    new stupidClient();
  }

  public stupidClient()
  {
    try
    {
      Socket s = new Socket("127.0.0.1",2013);//connect
      ObjectOutputStream output = new ObjectOutputStream(s.getOutputStream());//init stream

      //file to be uploaded
      File file = new File("C:\\Work\\radio\\upload\\(Op. 9) Nocturne No. 1 in  Bb Minor.mp3");
      long fileSize = file.length();
      output.writeObject(file.getName() + "|" + fileSize);//send name and size to server

      FileInputStream fis = new FileInputStream(file);//open file
      byte[] buffer = new byte[1024*1024];//prepare 1MB buffer
      int retVal = fis.read(buffer);//grab first MB of file
      int counter = 0;//used to track progress through upload

      while (retVal!=-1)//until EOF is reached
      {
        System.out.println(Math.round(100*counter/fileSize)+"%");//show current progress to system.out
        counter += retVal;//track progress

        output.writeObject("UPACK "+retVal);//alert server upload packet is incoming, with size of packet read

        System.out.println(""+buffer[0]+" "+buffer[1]+" "+buffer[2]);//preview first 3 bytes being sent
        output.writeObject(buffer);//send bytes
        output.flush();//make sure all bytes read are gone

        retVal = fis.read(buffer);//get next MB of file
      }
      System.out.println(Math.round(100*counter/fileSize)+"%");//show progress at end of file
      output.writeObject("UPLOAD_COMPLETE");//let server know protocol is finished
      output.close();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}

以下は私のサーバーコードです:

import java.net.*;
import java.io.*;

public class stupidServer
{
  Socket s;
  ServerSocket server;

  public static void main(String[] args)
  {
    new stupidServer();
  }

  public stupidServer()
  {
    try 
    {
      //establish connection and stream
      server = new ServerSocket(2013);
      s = server.accept();
      ObjectInputStream input = new ObjectInputStream(s.getInputStream());
      String[] args = ((String)input.readObject()).split("\\|");//args[0] will be file name, args[1] will be file size
      String fileName = args[0];
      long filesize = Long.parseLong(args[1]);

      String upack = (String)input.readObject();//get upload packet(string reading UPACK [bytes read])
      FileOutputStream outStream = new FileOutputStream("C:\\"+fileName.trim());

      while (!upack.equalsIgnoreCase("UPLOAD_COMPLETE"))//until protocol is complete
      {
        int bytes = Integer.parseInt(upack.split(" ")[1]);//get number of bytes being written
        byte[] buffer = new byte[bytes];
        buffer = (byte[])input.readObject();//get bytes sent from client

        outStream.write(buffer,0,bytes);//go ahead and write them bad boys to file
        System.out.println(buffer[0]+" "+buffer[1]+" "+buffer[2]);//peek at first 3 bytes received
        upack = (String)input.readObject();//get next 'packet' - either another UPACK or a UPLOAD_COMPLETE
      }
      outStream.flush();
      outStream.close();//make sure all bytes are in file
      input.close();//sign off
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  } 
}

いつもありがとうございます。

4

1 に答える 1

4

当面の問題はObjectOutputStream、ID メカニズムを使用して、ストリームを介して同じオブジェクトを複数回送信しないようにすることです。クライアントは の 2 回目以降の書き込みでこの ID を送信しbuffer、サーバーはキャッシュされた値を使用します。

この差し迫った問題の解決策は、reset()への呼び出しを追加することです。

output.writeObject(buffer);//send bytes
output.reset(); // force buffer to be fully written on next pass through loop

それはさておき、オブジェクトストリームの上に独自のプロトコルを重ねることで、オブジェクトストリームを悪用しています。たとえば、ファイル名とファイルサイズを「|」で区切られた単一の文字列として記述します。それらを 2 つの個別の値として記述するだけです。各書き込みのバイト数についても同様です。

于 2013-07-15T20:11:56.493 に答える