- ベストアンサー
サーブレットでレスポンスが返る仕組み
- サーブレットでレスポンスが返る仕組みについて疑問があります。具体的には、PrintWriterの使い方やクライアントへのレスポンスの仕組みについて知りたいです。
- クライアントにレスポンスを返すためには、PrintWriterを使用して文字列を出力する必要があります。
- 具体的な仕組みについては公開されているソースコードを読むか、サーブレットの仕組みについてのリファレンスを参考にすることをおすすめします。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
3番回答者です。 <-> これは、単なる対比(ファイル入出力の場合<->サーブレットの場合)の意味で入れただけです。わかりにくかったですか済みません。 ファイル読み書きは、サーブレットでも使う基本処理だと思ったのだけど、まだ、その手前だとは思わず。。。たぶん、サーブレットの解説サイトも、ストリームの読み書き(そのためのクラスやインターフェース構成)は、理解済みとして話を進めてるせいで、あんまり詳しく書かれてないのでしょうね、きっと。 >簡潔に書くと、PrintWriterインスタンスにstreamインスタンスがあり、 >そのstreamに対して出力している(もしくはrequestオブジェクトを渡す)ような >イメージで実装されているから、クライアントに出力される おおむねその理解でよいと思います。 「ストリーム入出力」の中に、ファイル読み書きや、サーブレットのRequestとResponse、コマンドライン入出力(標準入出力)、などなどが含まれており、ファイル読み書きがもっとも具体性がある(他はかなりブラックボックス化されてる)と思ったので、前回のように回答しました。 「ストリーム入出力」で調べるとよいかも、と思ったけど、結局ファイル入出力の解説になってるサイト多いですね。 あ、ちなみに、サーブレットでは、wr.close() すべきではないとのこと。かなり古めの解説ソースをコピーしちゃったので、クローズの話も入れてしまいました。済みません。 http://www.igapyon.jp/igapyon/diary/2004/ig040317.html
その他の回答 (7)
- Terry1940
- ベストアンサー率0% (0/1)
なぜprintwriterに文字をセットすることが、サーブレットコンテナが持っているバイト列バッファにデータをセットすることになるのか?の質問に対し: 図8-7を見て頂く、あるいは参考URLからダウンロードしたServletResponse.javaソース・コードの107行目からを見ればわかるように、このPrintWriterは、左側は通常のPrintWriterのメソッド群を用意していますが、上からはsetCharacterEncoding(これはContent-Typeヘッダ行もセットする)で指定されたエンコーディングを受け付けるようになっており、また右側にはユーザには見えない応答ごとに作られるバイト列バッファ(デフォルトで8192バイト)が繋がっています。「printwriterに文字をセットする」ということではなく、テキスト・データからコード変換し、バイト列に変換し、つながったストリーム出力(ここではバッファ)にセットするのがPrintWriterです。この仕様をどう実装するかは実装者の自由です。基本的にはこのPrintWriterは、java.io.PrintWriterを加工して、バイト列バッファをラップし、getCharacterEncodingで文字エンコーディングを呼び出すようにし、そしてバッファの管理とネットワーク送出機能を持たすことになります。getWriterメソッドはこれをインスタンス化して戻します。printlnなどのメソッドは単にコード変換した後でバイト変換してバッファに書くだけでなく、所定の長さになればcontent-lengthヘッダ行つきあるいはチャンク形式でそれをネットワークに送出する仕事も含まれることになります。 responseオブジェクトとPrintWriterオブジェクトがどこでどう繋がっているか?の質問に関して: 要求スレッドが連れてくる応答オブジェクトごとにこのPrintWriter(およびバイト列バッファ)のインスタンスを生成することになるから、ServletResponseインターフェイスにgetWriterメソッドを持たせるのは至極当然な話でしょう。応答ごとにこのPrintWriterオブジェクトを用意しないと大変なことになってしまいます。
お礼
ご回答ありがとうございます。 想像にするに非常にご聡明でよく理解されている方とお見受けしますが、 書かれている意味がよく分かりません。 8-7の図も意味がよく分かりません。これが何を言われようとしている図なのか。 もう少し、分かりやすい説明が不可能でしょうか。 不可能であれば、結構です。
- Terry1940
- ベストアンサー率0% (0/1)
サーブレット・コンテナが用意するPrintWriterオブジェクトに文字列をセットするということは、サーブレット・コンテナが持っているバイト列バッファにバイト・データをセットするということになります。それをどのようにネットワークに送出するかはサーブレット・コンテナの仕事になります。その要求スレッドがservice()を抜けた時点でそのバッファにデータがまだ残っていればそれをネットワークに送出します。 参考URLにあるチュートリアルの第8章に書いておきましたのでそちらを見てください。特に8.5.1項の図8-7を見れば分かりが早いでしょう。
お礼
ご丁寧にありがとうございます。 が、これをみても分からないというか、回答になっていません。 質問と回答がアンマッチしているためです。 知りたいのは、「printwriterに文字をセットすることがサーブレットコンテナが持っている バイト列バッファにデータをセットすることになる」ということではありません。 それは、動きをみればそんな感じあることは分かります。 質問は、「なぜprintwriterに文字をセットすることが、サーブレットコンテナが 持っているバイト列バッファにデータをセットすることになるのか」です。 言い方を変えると、responseオブジェクトとPrintWriterオブジェクトがどこで どう繋がっているかという質問です。
- mintia007
- ベストアンサー率59% (16/27)
No.5です。 >>そもそもsocket通信ってなんでしょうか? >>socketという言葉はありますが、socket通信という言葉はないようです。 http://www.ne.jp/asahi/hishidama/home/tech/socket/index.html http://e-words.jp/w/E382BDE382B1E38383E38388.html ・・・ インターネット検索すると沢山でてきますが。 >>socket(IPアドレス:ポート番号)で通信するということでしょうか。 全くもってその通りです。 >>またそれを、電話の例えることにより、余計わかりにくくなっています。 その人のツボにはまらないと当然、理解できませんよね。質問者様の的を得た他回答を永遠と お待ちください。 >>せっかくご回答いただいて申し訳なのいのですが、この説明ではよく理解できま >>せん。 ですから、そこまでえ言われるのでしたら、jakarta tomcatサイトにソースが掲載されてますので、 納得いくまでご勝手にお調べください。ソースにかかれている事が仕組みなのですから。 なんか、釣りっぽい質問なのか。
お礼
回答どうもありがとうございます。 >>またそれを、電話の例えることにより、余計わかりにくくなっています。 その人のツボにはまらないと当然、理解できませんよね。質問者様の的を得た他回答を永遠と お待ちください。 せっかくご回答いただいて申し訳ないのですが、技術的にしっかり理解されていて、 自信をもって、なおかつ、相手に伝わるような説明・回答出来る方からのだけの回答を求めております。 その理由は、そうでない方からの回答だと、間違って理解をする可能性があり、 それは回答をいただけないことより、もっともっと困るためです。 >ですから、そこまでえ言われるのでしたら、jakarta tomcatサイトにソースが掲載されてますので、 >納得いくまでご勝手にお調べください。ソースにかかれている事が仕組みなのですから。 はい。そのとおりです。 ただ、それを言うと、どんな問題でもそういう回答になりますよね。 自分で調べることが難しかったり、調べたことの確証を得たいから、 ここで質問しているのであって、しっかり回答できない方は回答すべきではないと思っています。 質問やこういうサイトは、質問者のためにあるので、回答者のためにあるのではないと思っています。 ですから、回答者は正確で自信を持って分かりやすい説明が出来ないのであれば、 それは回答すべきではありません。 よく理解していなかったり、下手な説明しか出来ないのに説明をすることは、 単なる回答者の自己満足であり、質問者する側からすれば、説明してもらえないほうがまだましです。 もちろん、そういう考え方ではない人もいますが、私はそういう考え方なのです。 回答してくださった方を批判するための質問では決してなく、とにかく、「正しいことを」を 知りたいだけであり、そういう回答が出来るかたからだけの回答を待っています。 それが出来る方がいないのであれば、回答が永遠にゼロでもそれはそれでかまいません。
- mintia007
- ベストアンサー率59% (16/27)
先ずはsocket通信が基本となります。 サーバー(サーブレット)⇔クライアント(ブラウザ)と言っても所詮はsocket通信なんですね。 soket通信とはクライアントとサーバー(C/S)とデータのやり取りをするという、人間同士で例えると 言わば会話と言えます。会話は基本中の基本で、何言語(英語、日本語等)で話すかという決まりが あると思います。その会話というのがC/Sのsocket通信でいうHTTPという言語で話をする訳です。 ここで電話で人と話をする事を考えてみます。先ずは話したい相手の電話番号を押して電話をかけ ますよね。 で、相手の電話のベルが鳴って相手が電話に出ます。そこから「もしもし」等の会話が始まる 訳です。これをC/Sのsocket通信に置き換えると、相手の電話番号がURL(http://localhost:8080/ など)となります(細かく言うとIPアドレスです)。もし内線番号がある場合、8080とかが内線番号 となります。通常はHTTP通信(ブラウザとの通信)の場合、80にておこなっています。その場合、80は 決まりきっているので、通常はhttp://localhost:80/とか内線(ポート番号)は付けずhttp://localhost/でアクセスしますよね。 で、電話の話に戻りますが相手と話をする際「もしもし」という言葉をかけるかと思います。 socket通信の場合、これが面倒で、1文字づつ送りだしてやる必要があります。 「も」「し」「も」「し」という感じです。この1文字づつ出して通信を行うのをストリームといい ます。ただ、これでは面倒ですよね。「もしもし」と単語をいっぺんに送りたいですね。 これを実現しているのがPrintWriterです。PrintWriterのインスタンスを取得し、 out.print("もしもし")としたほうが当然便利です。 ストリームだと一文字づづおくるので、ちょい手間がかかります。 socket通信ではサーバーとクライアント間で手を結びます。手を結んだ後は、話をする(通信をする) ために、ストリームが用意されます。ストリームは相手から来るストリーム(人間の会話で例える と耳)と相手へ出すストリーム(人間の会話で例えると口)の2つ取得する事ができます。 HttpServletReponse(親インターフェイスのServletResponseも含め)には複雑なのですが、 出力のストリームのインスタンス(OutputStreamクラス系の)をメンバ変数に持っています。 このストリームからPrintWriterのインスタンスを生成できます。これで、out.print()を使う事が できるわけです。 ここまでをまとめますと、 ・サーバー(サーブレット)⇔クライアント(ブラウザ)は単にsocket通信なんです。 ・socket通信に使っている言語はHTTPプロトコルとなります。 ・ポート(内線番号)は通常では80ですが、tomcatのデフォでは8080となっています。 ・会話は一言一言話すのではなく、単語で話しています(PrintWriter) です。 >>この手の仕組みを理解するには何を参考に勉強すればよいのでしょうか。 先ずはsocket通信の仕組みを勉強されてください。 サーブレットはデフォでシングルインスタンスマルチスレッドモデルの管理等、単純なsocket通信 だけではなく、スレッド管理もしていますので複雑です。 また、もしよろしければjakarta tomcatサイトにtomcatのソースが無償で配布されていますので、 覗いてみるのも手かもです。 更にsocket通信の中身まで知りたいというのであれば、ネットワーク関連を勉強してみてください。 ちなみに、javaの場合、基礎的なsocket通信に必要なAPIが揃っています。 (Socketクラス、ServerSocketクラス等) その中身までというのであれば、JDKをインストールしたディレクトリの中にsrc.zipという ファイルがありますので、その中身を見る必要がありますね。
お礼
詳しくご説明いただき、大変恐縮です。 真にありがとうございます。 でも、質問内容とsocket通信って関係ないですよね。 そもそもsocket通信ってなんでしょうか? socketという言葉はありますが、socket通信という言葉はないようです。 socket(IPアドレス:ポート番号)で通信するということでしょうか。 であれば、それは通信の上では、当然のことですよね。 一般に定義されてない言葉を使われることにより、どういう意味で使われているのかを すごく考えてしまいます。 またそれを、電話の例えることにより、余計わかりにくくなっています。 Javaを説明される多く方が、よく色んなたとえで説明されますが、例え話より 正確な話の方がよく分かります。 例え話と現実には共通部分もあれば違う部分もあり、また例えられる内容にもよって 人によって捕らえ方が違うため、返って理解の阻害になります。 ストリームには入力(耳)と出力(口)があるようですが、その出力(口)のインスタンスを メンバー変数としてHttpServletReponseは持っているのですよね。 そこまでは分かりますが、その出力(口)がPrintWriterインスタンスを生成できるとのことですが、 その出力(口)がPrintWriterインスタンスを生成できたら、なぜ、クライアントに出力できるのでしょうか。 せっかくご回答いただいて申し訳なのいのですが、この説明ではよく理解できません。
- mpro-gram
- ベストアンサー率74% (170/228)
ファイル書き込みと対比させてみるといいかも。HTTP通信にしてもファイル読み書きにしても、Streamでのやりとりなので、最終的に同じPrintWriterクラスから出力という形に統一されている。 1.ファイル open <-> HttpServlet open = 接続の確立(クライアントからの1件の要求) OutputStream out = new FileOutputStream(outfile); <-> HttpServlet継承クラスにおいて、メソッドの引数で、HttpServletResponse resとして出力ストリーム用インスタンスを受け取る ex. public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { } 2.ファイルstream から、PrintWriter取得 <-> 送信用にPrintWriter 取得 PrintWriter wr = new PrintWriter(new BufferedWriter( new OutputStreamWriter( out,"EUC-JP" ) )); <-> PrintWriter wr = res.getWriter(); /* ファイル書き込みでは、buffering用インスタンスを自分で用意しないといけないが、ServletResponseでは、既に設定済みのPrintWriterを返すメソッドがある。 getWriter メソッドの中で、PrintWriterインスタンスに現在の接続情報(Streamまたは、Writer継承インスタンス)を渡している。 よって、「responseのメンバー変数にもPrintWriterの参照を持たせている」ではなく、保持してるのは、PrintWriter側で、Streamインスタンスを保持。 */ 3.文字列出力 <-> 同じクラス型になってるので、同じ構文で出力(=送信)可能になる wr.println("string"); /* PrintWriterインスタンスは、自分が保持しているStreamインスタンス経由で文字列をそれぞれの接続先へ出力。 */ 4.ストリームのクローズ。PrintWriterインスタンスが、Streamインスタンスを持ってるので、PrintWriterからファイルストリーム や HttpServletResponse をもclose出来る。 wr.close();
お礼
ありがとうございます。 ただ、ファイル書き込みと比較することで、分かりにくくなっています。 ファイル書き込みを理解している人であればいいですが、そうでなければ、 まずファイル書き込みから理解し、それと比較して理解しないといけないので、 余計にわかりにくいです。 簡潔に書くと、PrintWriterインスタンスにstreamインスタンスがあり、 そのstreamに対して出力している(もしくはrequestオブジェクトを渡す)ような イメージで実装されているから、クライアントに出力されるということのようですね。 ちなみに、<-> はどういう意味でしょうか。通信を意味するのでしょうか・・・
(1).の仮説はまったくの見当はずれです。 (2).これを理解するには、 1.ソケット通信の仕組み 2.HTTPという仕組み の順で理解ができれば、自ずと回答が得られると思います。 >なぜ、outに文字列をセットするとクライアントへのレスポンスが返るかです。 まず、この考えは間違っています。 PrintWriter は、JavaBeans ではありません。 PrintWriter#println の引数に文字列を与えることが、インスタンス変数にセットする setter とは異なる意味をもつということを理解してください。
お礼
せっかくご回答いただき申し訳ないのですが、 言われている意味がわかりません。 おのずから回答が得られるのであれば、わざわざ質問しませんし、 この回答であれば、回答してくださらないほうがありがたいです。
- いけだ ひろし(@ike-2000)
- ベストアンサー率53% (69/129)
回答にはならないのですが考え方です。 JavaではHttpServletResponse(ServletResponse)をインターフェースとして定義しています。 ServletResponseの説明を読めば「文字データを送り返す場合は、getWriter() メソッドにより取得できる PrintWriter オブジェクトを使ってください。」とあります。 使う側に対してこのような説明をしているわけですから、このように使うのが正しいのです(もちろん使う側だけの理由で一方的に定義しているわけではないですが)。 さて、ServletResponseはインターフェースですからTomcatなどのサーブレットはこれを実装する必要があります。実装の仕方はTomcatに委ねられます。 よって、(1)の仮定は(2)にあるようにTomcatのドキュメント/ソース等を読まないと分からないというになります。 今回の場合はServletResponseで使い方が指定されているのでそれに従って使えばよいと考えます。 ただし、理解しようとすることは良いことだと思います。 私はその昔、1文字の文字列もStringのequalsを使ってコーディングし大失敗したことがあります。equalsが比較してくれると書いてあるからそのまま使いました。 ですがこのコードは全く不適切です。equalsの実行コストは途方もなく大きく実装されているからです(Stringでの実装は文字列のハッシュ値をまず算出し、マッチすればその後文字列比較を行います)。このような場合にはcharAtを使うべきでした。 仕様書に書いてあることは正しいのですがどのように使うかは使う側の問題です。 ServletResponseの場合には1通り(?確認していません)なのでこの通りに使えばよいと考えます。
お礼
どうもありがとうございました。 ただ、考え方ではなく、結論が頂きたいです。
お礼
回答、ありがとうございます。 >ファイル読み書きは、サーブレットでも使う基本処理だと思ったのだけど、まだ、その手前だとは思わず。。。>たぶん、サーブレットの解説サイトも、ストリームの読み書き(そのためのクラスやインターフェース構成) >は、理解済みとして話を進めてるせいで、あんまり詳しく書かれてないのでしょうね、きっと。 そうでしょうか。ファイルの読み書きは基本処理とは思えません。 今まで仕事でJavaで数万ステップ程度ですがコーディングをしてきましたが、ファイル読み書きなど 一度も書いたことありません。 ファイルの読み書きが基礎で、それを学んだ人がサーブレットを学ぶというのは、万人共通ではないと思いますよ。mpro-gram様がそうであっても、他の人はそうでない人たくさんいます。 解説サイトでそういうことを書いてないのも、多くの方が「なぜ動くのか」を考えずに、 ただ単に「こう書けば動く」ということだけを主眼においている(読む人の大多数もそれを求めている) からだと思います。 リンクのURLはなんでしょうか・・・ 拝見したのですが、今回の質問との関連がわからないのですが。