DESC
URI( Address )に含むことができない文字コードを使用できる文字に変換する
RFC では Percent-Encoding という名称
文字列データをURLに埋め込むようなときは
Webサーバとの間で正しく情報をやりとりするために
別の表記形式に変換する(エンコードする)必要がある
URI の規則を定める RFC 3986 では URI に
ASCII の非予約文字以外の文字データを用いる場合には
「%xx」( xxは16進数 )という形でコードを表記することが定められている
こんな問題がある
問題は.
日本語( 2 byte 文字 ) ServletEngine( Tomcat )で正しく変換されないこと
Client からの 日本語をうけつける際に, 文字化けをおこすこと
要は,
Gateway が 解釈できるように, 1 byte 文字に変化してしまう
データをURLに埋め込んでも問題が生じないように
英数字以外の文字や/などの記号を安全な形に変換することが目的
( さらに SPC, 制御文字も可視化する )
その際にどの文字コードを用いるかは実装によって異なる
Rule からはみ出る文字は, URLEncode( 16 進 ASCII 表記 ) される
SPACE < ->
Opera( Yahoo::jisyo ) で試してみた.
[+] ---> [%2B] # ASCII ですね.
[ ] ---> [+]
[&] ---> [%26]
[?] ---> [%3F] # ASCII ですね.
変換方法
1. Byte 単位でされる.
1. 第1 Byte -> 16 進.
1. 第2 Byte -> ASCII の文字範囲に収まる場合は, そのままの文字.
1. 各項目は [&] を接続文字になる
http://house.o-uccino.jp/detail_0006131464_h/
ただし、URL エンコードの変換方法は JavaScript と CGI では一部異なる
スペースは JavaScript の場合には %20 に変換されるが、
CGIでは + に変換される
URLEncoding が必要な理由
メール( SMTP )や HTTP などのパケット( Dataの塊 )は
ヘッダ部に宛先, その他メッセージの制御に関わる情報が追記されてます
このヘッダは途中のゲートウエーを幾つか中継され、端から端のノードに伝達されます
これらのノードに理解できるコードと文字で表現されなければならない
ヘッダ部に日本語のような 2 バイト文字が入ると、
ノードはこれを 1 バイトずつ解釈して誤解してしまう
これを避けるため URL エンコードができた
名前と値にある「安全でない」文字は"%xx"に変換する
"xx"はその文字のASCII値を16進表示したもの
しかし送られる情報をすべて「見える」文字列に変換するのは都合が良いことが多く
ボディ部分にも使われる
ボディ部分の変換にはもうひとつ
MIME(Multi-Purpose Internet Mail Extensions)エンコーディングがある
これは2バイトのバイナリ・データを3バイトの
7ビットASCII(American Standard Code for Information Interchange)
文字に変換するもので
マルチメディア情報の転送に使われる
RFC 2396 2.4
Browser で実行される URLEncode は htm file の文字コードでされるみたいです
Encode する文字
変換するのは
[ = & % + ]
プリントできない文字
[&] -> [%26]
[%] -> [%25]
[+] -> [%2B]
[=] -> [%3D]
[< ] -> [%3C]
[>] -> [%3E]
特別なルールとして
ASCII スペース[ ] -> [+]
Encode しない文字
IE をつかって google で検索をすると URLEncode の結果がみれます
".NET TIPS"
を検索したときには
検索結果の画面でのURL(IEの[アドレス]部分)が次のようになっているはずだ
このURLでは
検索文字列 ".NET TIPS" が
「%22.NET+TIPS%22」にエンコードされている(「"」が「%22」に
半角スペースが「+」に変換される
POINT
IE が自動的にエンコードをしてるが
プログラムで同様のリクエストを送信するときは
文字列データを自前でエンコードすること
URL に Encode が必要な文字を含めるには URLEncode 変換後の文字をいれる
"B&B" を URL に含めたい
"&" は変換する文字 -> "%26"
"B%26B"
変換するには, 単に 1byte ずつ %[16進数]といいかえるだけ
byte をなめるので 文字コードによって表記はかわる
POINt
% とついていないのは そのまま ということ
// ASCII Code は Encode しなくてもいいです
Lucky池田 < -> Lucky%92r%93c
// SJIS [あ] を UrlEncode
あああ < -> %82%A0%82%A0%82%A0
// [ニュース速報] を UrlEncode
EUC-JP
%A5%CB%A5%E5%A1%BC%A5%B9%C2%AE%CA%F3
SJIS
%83j%83%85%81%5B%83X%91%AC%95%F1
JIS
%1B%24B%25K%25e%21%3C%259B.Js%1B%28B
UTF-8
%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9%E9%80%9F%E5%A0%B1
逆に 何の Encode の URLEncode かを指定しないと元に戻せない
# [ あああ ] を 辞書ページで検索してみた. ( これこそまさに DB の機能が使えそう. )
# p = =%E3%81%82%E3%81%82%E3%81%82
# あ = %E3%81%82 となる.
# あ = E38182 ( これ何の文字コード ? )
elisp/urlencode/src/urlencode.el
* TextEditor で 文字をかいて , 特定の文字 Code で保存して
dump して % つけたもの
TIP
emacs では urlencode-region なんてものを 利用すると便利
すべての WebAddress を UTF-8 で Encode する
たいていの WebApplication 用の言語には URLEncode のメソッドがある
// C# VB.NET HttpUtilityクラス(System.Web名前空間
HttpUtility.UrlEncode( "あいうえお" )
// Java ( JSP )
この文字列が POST のボディ部分
あるいは GET のクエリとしてはめ込まれるのである
Tomcat は URL をデコードして Java の使用文字コードの Unicode に変換します
[ URL からのデータ ]
| Decode 前の文字Code は ISO8859_1 とみなして StringObject に変換
( Tomcat が URL を Decode して Java の内部 Code の Unicode に変換 )
|
|
[ StringObject ]
|
| < - ISO8859_1 の byte 列にもどす
| name.getBytes( "8859_1" );
URLDecode 直後の状態になる
|
[ Byte 列 ]
|
| Byte 列 を SJIS として, 再度 StringObject をつくる
| String( byteArray, "Shift_JIS" )
|
まとめ
Tomcat で日本語を使った JSP を利用するためには, 次の方法をする
String wordSjis = new String( word.getBytes("8859_1"), "Shift_JIS" );
1. getBytes(“8859_1”) で一旦 Unicode の文字列を
ISO8859_1(Latin-1) に変換したうえでバイト列へ
URLデコードしたときのバイト列が得られることになる
そのバイト列をString(bytes,”Shift_JIS”)のコンストラクタで
Shift_JIS のバイト列だと指定して
String型のオブジェクト(Unicode)をつくる
"Shift_JIS" の部分は
"EUC-JP" , "JISAutoDetect" もともと期待していたエンコーディングを指定する
"JISAutoDetect" は短い文字列の場合に正しく判定しないことがあるので使用しないほうが好ましい
まずしたうえでバイト列にする
そうするとURL デコードした直後のバイト列がとれます
そのうえでそのバイト列をString(bytes,”Shift_JIS”)のコンストラクタでShift_JISの
バイト列だと指定して String 型のオブジェクト(Unicode)を生成する
名前と値を=と&でつないでひとつの文字列にする
EX: name1=value1&name2=value2&name3=value3
htm の form で送っているやつか.
POINT
// 実は [?] 以降は HTTP のヘッダを渡していた.
http://server/foo.jsp?name=val&name=val&name=val&
■ MIME
DESC
MultiPurpose Internet Mail Expression ( 多目的 internet )
規格のひとつ. ( Internet で Data のやりとりをする時の規格 )
internet の 文字 Code は 7bit
だから binary を扱うときは uuencode で変更して uudecode で戻していた
でも internet が普及したので Programmer でない人がこれをするのは不便
そこで MIME という規格をつくり, Binary を扱えるようにした
ボディ部分の変換にはもうひとつ
MIME(Multi-Purpose Internet Mail Extensions)
エンコーディングがある( これも Encode )
1. MultiPurpose Internet Mail Expression ( 多目的 internet )
これは2バイトのバイナリ・データを3バイトの7ビットASCII(American Standard Code for Information Interchange)文字に変換するもので
マルチメディア情報の転送に使われる
( binary から ASCII に置換するのが目的 )
■ 基本的なこと
■ Socket
DESC
Socket とは ユーザにとってのデータの出入り口
ユーザはソケットへデータを読み書きするだけで通信できる。
( とりあえず FILE Pointer みたいなものだと思えば OK )
Socket の裏側の通信 Protocol を意識しなくても通信Programができます
Socket は 2 つ以上の Soket が関係をもって初めて意味があります
( 1 : N であることもある )
Socketの種類
DESC
2 つあります
Block とは
パケットがくるまでスレッドがブロックされるモードのこと
パケット到着まで他の作業はできません
受信では
アプリケーションが
WSARecv 関数か WSARecvFrom ()に受信用のメモリを提供する
受信前に一つ以上のメモリを用意しておくと
データが到着したらそのメモリにすぐ配置される
それらの関数を使えば recv 関数か recvfrom 関数の呼びだし時にコピーしなくて済む
受信用メモリを提供したときに
すでにデータがあれば
すぐにそのメモリにコピーされる
アプリケーションが受信用メモリを用意していないときにデータが到着すると
ネットワークがお決まりの形式で同期的に処理します
内部のメモリに保存します
アプリケーションが
setsockopt 関数で受信用の一時メモリの大きさをゼロに設定した場合は例外です
信頼性が保証されている通信規格では
アプリケーションがメモリを用意するとその分のデータを受信できる
信頼性が保証されていない通信規格ではデータを失う
■ Packet
DESC
バイト列のことをネットワーク上ではパケットという。
送信されるデータだけではなく、宛先などの制御情報が含まれる。
この情報をみてルータなどは制御をする。
小包での宛先と中身みたいなもの。
■ Protocol
DESC
通信プログラムが情報を交換する際の手順や, パケットの内容についての決まりごと
ネットワークで通信しあうための各ルールを階層構造のモジュール単位でわけている。
これを プロトコルスイート, プロトコルファミリという ( TCP/IP など )
[ ソケット ]
[ HTTP ]
[ TCP ][ UDP ]
[ IP ]
■ ApplicationProtocol
DESC
TCP/IP プロトコルがしてくれるのは
データを送信したり、受信をすることだけ。
実際のソケットを使ったプログラムでは
やり取りする情報をどんな形式で、いつどちらから送るかなどの取り決めが必要になる。
これをアプリケーションプロトコルという。
■ Order
POINT
ソケット経由で多バイトのデータを送信する場合は
アドレスの昇順で送る。
受信側も同じく昇順で受信する必要がある。
昇順とは data の小さい順番に並べること。階段でいうと上にあがるのと同じ
送信側と受信側のプロセッサ固有のバイトオーダーが逆になると問題がおきる
POINT
あるマシンから別マシンへ多バイトのバイナリ値を送る場合は hton*, ntoh * を使うこと。
もし ネットワークのバイトオーダーとプロセッサ固有のオーダーが同じ場合は
変換処理はされない。
■ バッファリング
POINT
recv(), send() は一対一対応をしていない。
TCPコネクション上を流れるデータは全体でひとつのデータ列とみなせるが
3箇所のバッファに分かれて存在する。
send() -> [ SendQ ] ---> [ RecvQ ] --(recv())--> [ Deliverd ]
send() は SendQ にすべてのデータが入るまでブロックされる。
複数の send() が1回の recv() で受け取ることがあるため
受信するときは メッセージの解析をすることが必須。
通常は 区切り文字でする。
■ ソケットの実装
POINT
TCP ソケットの理解するには ソケット構造体やプロトコルの仕組みを理解することが大切
ソケット構造体は次の情報をもつ
[ローカルIPアドレス]
[ローカルポート]
[リモートIPアドレス] // ローカルソケットの接続先を指定する
[リモートポート]
[受信バッファ]
[送信バッファ]
POINT
ローカルポートは bind() で明示できる。また初めて使用する際に自動でアサインされる。
サーバは bind() をコールして明示して、クライアントはお任せするのが普通。
■ ソケットの状態
[ closed ] ---> [ ]
POINT
send(), recv() に対応関係があるとは限らない
1回のsend() を複数の recv() で受けとったり
3回のsend() を1回の recv() で受けとることもある
POINT
TCP ソケットで send(), recv() を呼び出せるのは 状態が Establishing のときだけ
connect()
bind() を指定していないので 他のソケットが利用していないポートが自動でアサインされる
[ Closed ] -> [ Connecting ] -> [ Establishing ]
[ ] [ P ]
[ ] [ P ]
[ ] [ P ]
[ ]
サーバ側
bind() listen
[ Closed ] -> [ Closed ] -> [ listening ]
[ ] [ Q ]
[ ] [ ]
[ ] [ ]
[ ]
connect があった場合に新規ソケットを作成して IP, リモートポートが割り当てられる
接続可能なソケットリストに入る
accept() は Establishing なソケットをひとつ返す
->
[ Establishing ]
[ ]
[ ]
■ 切断処理の流れ
POINT
TCP には正当な切断処理という仕組みがある
これにより アプリケーションは転送中のデータを損失せずに済む。
POINT
切断は双方向に行うことができる。
close(), shutdown() は データ送信の終了を表明する。
close() を受けた TCP 側は 送信バッファの情報の残りをすべて送信して
切断の意思(ハンドシェイク)を送信する。
これで受け手はこれ以上受信バッファにデータがこないことを確認できる。
これが recv = 0 ( close() )の意味
肯定応答があれば送信した側の情報がすべて 受信バッファに格納されたことが確認できる。
コネクションは Half-Colsed の状態になる
これを双方に行うことで完全に切断する。
POINT
ディスクリプタの割り当てが解除されても ソケット自体は TCP に残る。
これが Time-Wait の状態。
Time-Wait がある意味は ソケット構造体が存在し続けて
他のプログラムが同じポートのソケットをバインドすることを防ぐことにある。
( EADDINUSE を返す )
POINT
後から接続を解除する場合
相手からの解除要求がきて肯定応答をすると Close-Wait 状態に移行する
[ Establishing ] ---> [ Close-Wait ]
このとき Application の close() 待ちになる。
close() を呼ぶと ソケット構造体へのディスクリプタが解除される。
そして肯定応答が返ると ソケット自体が消滅する。
( つまり 互いにすべての情報を相手側に送れたことを確認できるため )