web-dev-qa-db-ja.com

Javaネットワーキング:ソケットのInputStreamおよびOutputStreamについて説明する

ServerSocketを使用してサーバーを作成しました。その後、Socketを使用してクライアントを作成し、このサーバーに接続します。

その後、InputStreamで「何か」を行い、OutputStreamをSocketオブジェクトから取得します。しかし、私はinputStreamとoutputStreamをあまり理解していません。これが私の簡単なコードです:

private Socket sock = null;
private InputStream sockInput = null;
private OutputStream sockOutput = null;

...
            String msg = "Hello World";
            byte[] buffer = null;

            try {
                sockOutput.write(msg.getBytes(), 0, test.length());
                sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
                buffer = new byte[test.length()];
                sockInput.read(buffer, 0, test.length());
                System.out.println(new String(buffer));
                sockInput.read(buffer, 0, test.length());
                System.out.println(new String(buffer));
            } catch (IOException e1) {
                e1.printStackTrace();
                }

結果は「Hello World」と「Hello StackOverFlow」になります。

サーバー側のコードは次のとおりです。

private int serverPort = 0;
    private ServerSocket serverSock = null;

    public VerySimpleServer(int serverPort) {
        this.serverPort = serverPort;

        try {
            serverSock = new ServerSocket(this.serverPort);
        }
        catch (IOException e){
            e.printStackTrace(System.err);
        }
    }

    // All this method does is wait for some bytes from the
    // connection, read them, then write them back again, until the
    // socket is closed from the other side.
    public void handleConnection(InputStream sockInput, OutputStream sockOutput) {
        while(true) {
            byte[] buf=new byte[1024];
            int bytes_read = 0;
            try {
                // This call to read() will wait forever, until the
                // program on the other side either sends some data,
                // or closes the socket.
                bytes_read = sockInput.read(buf, 0, buf.length);

                // If the socket is closed, sockInput.read() will return -1.
                if(bytes_read < 0) {
                    System.err.println("Server: Tried to read from socket, read() returned < 0,  Closing socket.");
                    return;
                }
                System.err.println("Server: Received "+bytes_read
                                   +" bytes, sending them back to client, data="
                                   +(new String(buf, 0, bytes_read)));
                sockOutput.write(buf, 0, bytes_read);
                // This call to flush() is optional - we're saying go
                // ahead and send the data now instead of buffering
                // it.
                sockOutput.flush();
            }
            catch (Exception e){
                System.err.println("Exception reading from/writing to socket, e="+e);
                e.printStackTrace(System.err);
                return;
            }
        }

    }

    public void waitForConnections() {
        Socket sock = null;
        InputStream sockInput = null;
        OutputStream sockOutput = null;
        while (true) {
            try {
                // This method call, accept(), blocks and waits
                // (forever if necessary) until some other program
                // opens a socket connection to our server.  When some
                // other program opens a connection to our server,
                // accept() creates a new socket to represent that
                // connection and returns.
                sock = serverSock.accept();
                System.err.println("Server : Have accepted new socket.");

                // From this point on, no new socket connections can
                // be made to our server until we call accept() again.

                sockInput = sock.getInputStream();
                sockOutput = sock.getOutputStream();
            }
            catch (IOException e){
                e.printStackTrace(System.err);
            }

            // Do something with the socket - read bytes from the
            // socket and write them back to the socket until the
            // other side closes the connection.
            handleConnection(sockInput, sockOutput);

            // Now we close the socket.
            try {
                System.err.println("Closing socket.");
                sock.close();
            }
            catch (Exception e){
                System.err.println("Exception while closing socket.");
                e.printStackTrace(System.err);
            }

            System.err.println("Finished with socket, waiting for next connection.");
        }
    }

    public static void main(String argv[]) {
        int port = 54321;
        VerySimpleServer server = new VerySimpleServer(port);
        server.waitForConnections();
    }

私の質問は:

  1. sockOutput.writeを使用すると、sockInput.readでそれらのメッセージを取り戻すことができます。それで、それらのメッセージは保存されましたよね?これがtrueの場合、作成したサーバーに保存されますか、それともSocket Objectなどの他の何かに保存されましたか?.

  2. ソケット文字列A1、A2、...に書き込んだ場合、それぞれA1、A2、...文字列を受け取りますよね?

12
hqt

ソケットは、ネットワークを介して何かと通信するために使用する抽象概念です。下の図を参照...

Javaでは、ソケットを介してデータを送信するには、ソケットからOutputStream(1)を取得し、OutputStreamに書き込みます(データを出力します)。

ソケットからデータを読み取るには、そのInputStreamを取得し、この2番目のストリームから入力を読み取ります。

ストリームは、壁のソケットに接続された一方向パイプのペアと考えることができます。壁の反対側で何が起こるかはあなたの問題ではありません!

あなたの場合、サーバーには別のソケット(接続のもう一方の端)と別のストリームのペアがあります。 InputStream(2)を使用してネットワークから読み取り、OutputStream(3)を使用してsameネットワーク経由でクライアントにデータを返し、クライアントはInputStreamを介して再度読み取ります(4)往復を完了します。

      Client                                                     Server

1. OutputStream -->\                                     /--> 2. InputStream -->
                    Socket <--> network <--> ServerSocket                       |
4. InputStream  <--/                                     \<--3. OutputStream <--

更新:コメントへの返信:

ストリームとソケットは生のバイトを送信するだけであることに注意してください。この抽象化レベルでは、「メッセージ」の概念はありません。したがって、Xバイトと別のXバイトを送信し、次にXバイトを読み取り、別のXバイトを読み取ると、システムは、2つのメッセージがあるかのように動作します。

Xバイトを送信し、別のXバイトを送信し、長さが2Xの応答を読み取ると、単一の結合された「メッセージ」を読み取ることができる 、しかし気づいたように、ストリームの基礎となる実装はバイトのチャンクをいつ配信するかを選択できるため、Xバイト、次にXバイト、または2Xを一度に、または0.5Xを4回返す場合があります。

36
DNA

InputStreamOutputStreamは、2つの完全に異なるストリームです。片方に書き込むものには、もう片方から読み取るものとは関係ありませんアプリオリInputStreamは、サーバーが送信することを決定したすべてのデータを提供します。私もあなたのコードのこの部分についてコメントしたいと思います:

_sockOutput.write(msg.getBytes(), 0, test.length());
sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
_

文字列test(コードには表示されません)の長さを使用します。これは、最初の引数として渡すバイト配列とは関係ありません。これにより、意図したメッセージがArrayIndexOutOfBoundsExceptionまたは切り捨てられる可能性があります。

更新された質問への追加コメント

サーバー側のコードを確認すると、コードが正しく記述されていません。エラー後および正常に完了したときの適切なクリーンアップを保証するには、try { handleConnection(...); } finally { socket.close(); }が必要です。コードがサーバー側で何かを閉じることはありません。

最後に、最も重要なことに、コード全体がデッドロックを引き起こす可能性のある方法で記述されています。通常、読み取りと書き込みには別のスレッドが必要です。そうでない場合、次のことが発生する可能性があります。

  1. 出力にデータを書き込もうとしました。
  2. サーバーはそれを読み取り、入力内のデータで応答しようとします。
  3. ただし、バッファが小さすぎるため、サーバーが最初に何かを送信してから残りを受信するため、すべてを送信することはできません。しかし、あなたが持っているものすべてを送る前に、あなたは受信部分に到達しません。
5
Marko Topolnik