saba1024のブログスマートフォン用の表示で見るsaba1024のブログどうでも良い思いついた事とかプログラミング関係のメモとか書いていきます。この広告は、90日以上更新していないブログに表示しています。2017の2日目の記事です。この記事は、以下の3つの投稿で成り立っています。動的な値を返す初めにGo言語やRust等、システムよりのプログラミングに利用しやすい言語の勢いが出てきている昨今、Webサーバ自体を実装するというサンプルをよく目にするようになりました。Groovyで自分でもWebサーバを実装してみました。Webサーバの動作をざっくり理解するためのものなのでセキュリティーなどは一切考慮していません。全体ソースをご覧ください。今回は最も基本的な部分を理解するための初回なので、ほぼほぼJavaのコードになります。クライアントから接続されたら、別スレッドでその要求を処理する。KeepAlive(HTTP1.1)の場合、そもそもストリームの終わりがないので、2回連続CRLFが現れたらヘッダーの終わりと判断する。クライアントから接続されたら、別スレッドでその要求を処理する。...省略...}上記のコードが、実際にGroovyでサーバを8081番ポートで起動して、ユーザからのリクエストを待つ処理になります。クライアントからのリクエストの度に、それぞれ別スレッドでServer02#run()が実行されます。ServerSocketインスタンスで、サーバを起動してはいますが、この時点では別にWebサーバでもないし、FTPサーバでもDBサーバでもありません。単純に指定したポートでクライアントからの通信を待ち受けるプログラムになります。このプログラムが、クライアントから送られてくるバイト列を解析して、処理して、クライアントにその結果を返す、という一連の流れをHTTPというルールに則って行うことで初めてWebサーバとなります。続いてこのプログラムをHTTPサーバにするための2大項目をそれぞれ見ていきます。HTTPリクエストの処理ユーザから送られてくるバイト列を、HTTPのルールに則って解析します。KeepAlive(HTTP1.1)の場合、そもそもストリームの終わりがないので、2回連続CRLFが現れたらヘッダーの終わりと判断する。}HTTPリクエスト自体はシンプルなもので、以下のような物がクライアントから送られてきます。application/x-www-form-urlencoded------------------------------{a:b}&message:どうですかこれは実際にこのプログラムを起動して、curlコマンドでアクセスした時に表示される内容です。今回は最も低レベルなInputStream#read()を使って、クライアントから送られてくるリクエスト(HTTPリクエスト)を1バイトずつ読み込んで処理しています。注意する点としては、ブラウザなどのクライアントが、keepAliveをオンとしてアクセスしてくると、InputStream#read()は終わらない(-1を返さない)ので、無限ループになってしまう点です。HTTPの仕様として各ヘッダー行はCRLFで終わり、HTTPリクエストヘッダーは空行(CRLFのみ)で終わる、と決められているので、InputStream#read()で読み込んだ直近の4Byte(本当はIntegerだけど0-255の値なので実質Byte)を自作のQueueに貯めこんでいます。直近の4ByteがCRLFCRLFの場合、それはHTTPリクエストヘッダーの終わりとういことなので、ヘッダーの読み込みを終了するようにしています。HTTPリクエストボディもしHTTPリクエストヘッダー内にContent−Lengthが指定されている場合、HTTPリクエストボディが存在します。InputStreamインスタンスはread()でどこまで読み込まれたかを保持していますので、引き続きInputStream#read()をCotnent-Length分実行してあげることで、HTTPリクエストボディ部分が取得できます。1点注意しなければならないことが有ります。マルチバイト文字です。ASCIIであれば、1Byteずつデータを取り出して、それを1文字として扱っても問題ありませんが、日本語を始めマルチバイト文字が送られてくる可能性のあるHTTリクエストボディはそれでは不味いです。そのため、InputStream#read()で取得したByteデータはそのまま即1文字として処理せずに、Integerのリスト等に貯めこんでおいて、あとで一気にString型に変換して上げる必要が有ります。GroovyでもJavaのプリミティブ型の配列(byte[])にasで簡単に変換することが出来ます。String()されています。)HTTPレスポンスの処理さて、クライアントからのリクエストを解析したら当然最後にはクライアントに何かデータを返して上げる必要が有ります。}やっていることは言ったってシンプルで、クライアントに返したい情報をHTTPレスポンスのルールに則ってStringで記述しておいて、それを1文字ずつIntegerに変換してOutputStream#write(int)に渡してあげているだけです。これはあくまで最低限ブラウザがHTTPレスポンスを一応HTMLとして解釈して表示してくれる最低限のデータを指定しているだけなので、実際のHTTP1.1のルール通りにはなっていません。まとめいかがだったでしょうか。HTTPサーバを立てるだけなのに結構大変ですね。本来は正しいHTTPの仕様に準拠する必要も有りますしセキュリティーの問題も有ります。このように自分でソケットサーバ/HTTPサーバを実装してみることで色々理解が進みますね。2017の1日目の記事です。月別アーカイブはてなブログをはじめよう!saba1024さんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。