1

サーバーとクライアントがサーバーからクライアントにファイルを転送するためのコードをいくつか書きましたが、それは魅力的に機能しました。しかし、いくつか質問があります。GUI の下でこのコードを作成したいのですが、フォルダー上のすべてのファイルを一覧表示したいのですが、提供されたファイルの一覧を確認した後、クライアントに必要なファイルを選択させるにはどうすればよいですか (文字列をに送信するにはどうすればよいですか)ファイルを選択するためにサーバー)?

サーバーコード

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



class TCPServer {

    public static void listfile(){

    File folder = new File("c:/");
    File[] listOfFiles = folder.listFiles();

    for (int i = 0; i < listOfFiles.length; i++) {
      if (listOfFiles[i].isFile()) {
        System.out.println("File " + listOfFiles[i].getName());
      } else if (listOfFiles[i].isDirectory()) {
        System.out.println("Directory " + listOfFiles[i].getName());
      }
    }
  }


    public static void main(String args[]) {

        listfile();

        while (true) {
            ServerSocket welcomeSocket = null;
            Socket connectionSocket = null;
            BufferedOutputStream outToClient = null;

            try {
                welcomeSocket = new ServerSocket(3248);
                connectionSocket = welcomeSocket.accept();
                outToClient = new BufferedOutputStream(connectionSocket.getOutputStream());
            } catch (IOException ex) {
                // Do exception handling
            }


            if (outToClient != null) {

                String FileName = "carexception.java";

                File myFile = new File("C:\\"+FileName);

                byte[] mybytearray = new byte[(int) myFile.length()];

                FileInputStream fis = null;

                try {
                    fis = new FileInputStream(myFile);
                } catch (FileNotFoundException ex) {
                    // Do exception handling
                }
                BufferedInputStream bis = new BufferedInputStream(fis);

                try {
                    bis.read(mybytearray, 0, mybytearray.length);
                    outToClient.write(mybytearray, 0, mybytearray.length);
                    outToClient.flush();
                    outToClient.close();
                    connectionSocket.close();

                    // File sent, exit the main method
                    return;
                } catch (IOException ex) {
                    // Do exception handling
                }
            }

        }
    }
}

クライアントコード

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

class TCPClient {

    public static void main(String args[]) {
        Scanner s = new Scanner(System.in);
        byte[] aByte = new byte[1];
        int bytesRead;

        Socket clientSocket = null;
        InputStream is = null;

        try {
            clientSocket = new Socket("127.0.0.1", 3248);
            is = clientSocket.getInputStream();
        } catch (IOException ex) {
            // Do exception handling
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        if (is != null) {

            FileOutputStream fos = null;
            BufferedOutputStream bos = null;
            try {
                fos = new FileOutputStream("E:\\sss.java");
                bos = new BufferedOutputStream(fos);
                bytesRead = is.read(aByte, 0, aByte.length);

                do {
                        baos.write(aByte);
                        bytesRead = is.read(aByte);
                } while (bytesRead != -1);

                bos.write(baos.toByteArray());
                bos.flush();
                bos.close();
                clientSocket.close();
            } catch (IOException ex) {
                // Do exception handling
            }
        }
    }
}
4

4 に答える 4

3

目標を達成するには、かなり多くのことを変更する必要があります。

サーバーが何かを行うためにクライアントがサーバーに要求を送信する必要があるという意味で、特定のプロトコル順序を想定できます。そのため、サーバーは接続が確立されたときに常にリッスン状態になります。

あなたがすべき、

  1. リクエストの送信とレスポンスの受信のループを導入する
  2. 文字列オブジェクトを送信する方法を理解する
  3. ファイル送信部分を分割して、OS が処理できるよりも大きなバイト配列を割り当てないようにします (たとえば、ファイルが 4GB であると考えてください。ファイル全体にバイト配列を割り当てるのは面倒です)。

だから、これを念頭に置いて、私たちは始めることができます. ステップ 1 に関しては、これは while ループを使用して実現できます。サーバーが常にリクエストをリッスンしていると仮定すると、サーバーの「リクエスト ループ」は次のようになります。

ClientRequest request;
while (request.getType() != RequestType.Complete) {
    // receive new request
    // depending on type, send response
}

ここでは単純に 2 つのクラスを追加しました。1 つClientRequestはクライアントからのメッセージをカプセル化するクラスで、もう 1 つRequestTypeはクライアントが関心を持つ要求のタイプ (ファイル リストやファイル コンテンツなど) を定義する列挙型です。

public enum RequestType {
   None, Complete, RequestFileList, RequestFileContent
}

public class ClientRequest {
   private RequestType type;
   public ClientRequest() {
       type = RequestType.None;
   }

   public RequestType getType() {
       return type;
   }
}

これを何らかの方法でソケットにアタッチする必要があるため、リクエストを受信するメソッドを追加し、そのリクエストを現在のリクエスト インスタンスに割り当てます。

ClientRequest request = new ClientRequest();
while (request.getType() != RequestType.Complete) {
    // receive new request
    receiveRequest(clientSocket.getInputStream(), request);
    if (request.getType() != RequestType.Complete) {
        // pick a response
    }
}

private void receiveRequest(DataInputStream socketStream, ClientRequest request) {
    // get a type of request
    byte type = socketStream.readByte();
    request.setType(RequestType.from(type));
    // get parameters for request, depending on type
    if (request.getType() == RequestType.RequestFileContent) {
        // receive file id (file name for instance, or some other id that you prefer)
        String argument = readString(socketStream);
        request.setArgument(argument);
    }
}

fromこれにより、バイトをリクエストに変換する RequestType のメソッド、 のsetTypeメソッドClientRequest、およびメソッドが追加されますreadString。また、ClientRequest に新しいフィールドと対応する get メソッドと set メソッドを追加します。

public enum RequestType {
    // types as before
    ;
    public static RequestType from(byte b) {
        switch (b) {
            case 1: return RequestType.Complete;
            case 2: return RequestType.RequestFileList;
            case 3: return RequestType.RequestFileContent;
            default: return RequestType.None;
         }
    }
}

public class ClientRequest {
    private String argument;
    public void setType(RequestType value) {
        type = value;
    }

    public String getArgument() {
        return argument;
    }

    public void setArgument(String value) {
        this.argument = value;
    }
}

private String readString(DataInputStream socketStream) {
    int length = socketStream.readInt();
    byte[] stringBytes = new byte[length];
    socketStream.read(stringBytes);
    return new String(stringBytes, "UTF-8");
}

次に、リクエストに応答する次のステップに進みます。switch ケースを追加して、リクエストのタイプを処理するだけです。

{
    // depending on type, send response
    handleRequest(clientSocket.getOutputStream(), request);
}

private void handleRequest(DataOutputStream socketStream, ClientRequest request) {
    switch (request.getType()) {
        case RequestType.RequestFileList: {
            String[] fileList = getFileList(getCurrentDirectory());
            // send response type
            socketStream.write(ResponseType.ResponseFileList.getByte());
            // send number of files
            socketStream.writeInt(fileList.length);
            // send each string
            for (String fileName : fileList) {
                sendString(socketStream, fileName);
            }
        }
        break;
        case RequestType.RequestFileContent: {
            // send response type ResponseType.ResponseFileContent
            // send length of file so other party can determine number of bytes to receive
            // send file contents in chunks of a fixed byte array length
            // send last part of file contents, if length of file is not evenly divided by array chunk size
        }
        break;
    }
}

sendString メソッドは、単に readString メソッドの「逆順」です。

private void sendString(DataOutputStream socketStream, String value) {
    int length = value.length();
    socketStream.writeInt(length);
    byte[] stringBytes = value.getBytes("UTF-8");
    socketStream.write(stringBytes);
}

ResponseTypeにあるものと同様の値の列挙であるRequestTypeため、クライアントはサーバーが送信する応答のタイプを処理できます。

これらの変更により、ファイル リストを要求し、サーバーが送信するファイルの応答を表示できるようになります。ユーザーが受信するファイルを選択すると、クライアントは新しい要求をサーバーに送信でき、サーバーは適切なファイルの内容をクライアントに送信できます。

クライアント アプリケーションは、サーバーがソケット ストリームからの読み取りおよび書き込み用に指定した対応するメソッドを使用して、同様ClientRequestのクラス (おそらく という名前) を定義する必要があります。ServerResponseこれは、ソケットをクラスにカプセル化することでさらに抽象化できます。これには、GUI がサブスクライブできる要求または応答を受信したときのリスナー パターンがありますが、これは私の例を超えています。

何か明確にする必要があると思われる場合は、コメントを残してください。できる限りお答えします。

于 2013-01-07T13:57:41.450 に答える
1

どのようにファイルを尋ねますか? 名前で!サーバーはコマンドを受け入れ、応答で応答すると思います。サーバーへのコマンドには次の形式を使用できます: CMD_NAME, arg1, arg2, ... 引数は、コマンドに応じてバイナリにすることができます。CMD_NAME によって、サーバーは必要なものを区別します (ファイルを受け入れるか、ファイルを提供します)。

1 種類のリクエストしか受け付けないという問題があります。サーバーからさまざまなリクエストを行うには、コマンド メカニズムが必要です。サーバーは、ハードコーディングされた応答をすぐに返すのではなく、これらの要求を解析する必要があります。これにより、柔軟になります。

http://www.javamex.com/tutorials/networking/simple_server_s_side.shtmlこのような例は他にもたくさんあると思います。Java ソケットは信頼性が高く、問題はありません。クライアントとサーバーの間でさまざまなメッセージをやり取りする方法の基本を学び始めてください。しかし、あなたの問題はソケット関連ではありません。ファイルを介して通信することを想像してください。あるファイルから要求を読み取り、別のファイルに応答を書き込みます。その時、あなたはどんなメッセージを書きますか?これをプロトコルと呼びます。シンプルなものを設計する必要があります。

于 2013-01-06T18:57:15.973 に答える
0

配列を作成しようとしたので、各ファイルには独自のインデックスがあります...クライアントが必要なファイルを選択すると、特定の配列インデックスでファイルを返します。

〜ところで、配列をシリアル化してクライアントに送信できます。

于 2013-01-06T18:24:34.913 に答える
0

を使用ObjectOutputStreamして、文字列またはその他の種類を送信ObjectできますwriteObject

于 2013-01-06T19:50:27.023 に答える