Java + Windows の Socket 通信
Java + WindowsにてSocket通信を行うAPの開発が必要となったので、実験メモ。
要件
サーバ側要件
クライアント側要件
- 一定時間待ち行列上に存在した場合は、クライアントは依頼を送信せずに、処理を諦めたい
- クライアントにて異常と判断した場合は、可能な限りサーバ側にて処理が行われないようにしたい
- サーバに依頼した処理が仕掛ってしまった場合は、処理の中断はあきらめるものの、サーバ側に諦めたことを伝えたい
普通はほとんどありえない要件ですが、今回ばかりは特別な事情ということで…
プロトコル(1)
- 接続: クライアント#connect -> サーバ#accept
- 要求: クライアント#send -> サーバ#recv
- 結果: クライアント#recv <- サーバ#send
- 切断: クライアント#close , サーバ#close
なお、recvのタイムアウトを5秒とする。以降、xx秒経過を(xx)と表現。
検討/実験(1−A)
backlogに滞留しているケース。
<クライアント側の状況>
- 接続(00): クライアント#connect -> OK
- 要求(00): クライアント#send -> OK
- 結果(05): クライアント#recv -> Timeoutエラー
- 切断(05): クライアント#close -> OK
これでは、送信した依頼はまだ実行されずにタイムアウトしたのか、それともサーバ側の処理時間が長くてタイムアウトしたのかが分からない。
というのも、syn ⇒ syn/ack ⇒ ackの流れは、ServerSocket#bind(listen)すると、OS側で自動で行われるため、クライアント側のconnectタイムアウトでは、サーバ側のaccept前でのタイムアウトか、accept後でのタイムアウトかを判定できない。
しかも、send/flushは成功している以上、サーバ側に依頼を受け取って貰えたように見える。
だが、処理結果は受信できなかったため、依頼は失敗したとして、クライアント側の処理を継続する必要がある。
<サーバ側の状況>
- 接続(10): サーバ#accept -> OK
- 要求(10): サーバ#recv -> OK
- 結果(10): サーバ#send -> OK
- 切断(10): サーバ#close -> OK
クライアントからのacceptに成功し、依頼を受信したので、処理を実行し、結果を応答できた。すばらしい。
<結果>
クライアントは処理を失敗として扱い、さらに、サーバ側で処理してしまったかどうかが判断できない。サーバ側は処理を成功として扱った。結果、クライアントとサーバ間では、状態の不整合が発生する。これでは、よろしくない。
プロトコル(2)
- 接続: クライアント#connect -> サーバ#accept
- 確認: クライアント#recv <- サーバ#send
- 要求: クライアント#send -> サーバ#recv
- 結果: クライアント#recv <- サーバ#send
- 切断: クライアント#close , サーバ#close
検討/実験(2−A)
backlogに滞留しているケース。
<クライアント側の状況>
- 接続(00): クライアント#connect -> OK
- 確認(05): クライアント#recv -> Timeoutエラー
- 要求(--): クライアント#send -> skip
- 結果(--): クライアント#recv -> skip
- 切断(05): クライアント#close -> OK
とりあえず、要求を送信していないので、サーバ側で処理していないということは確実に判定できる。すばらしい。
<サーバ側の状況>
- 接続(10): サーバ#accept -> OK
- 確認(10): サーバ#send -> OK
- 要求(10): サーバ#recv -> 即時エラー
- 結果(--): サーバ#send -> skip
- 切断(10): サーバ#close -> OK
要求を受信できなかったので、とりあえず、クライアント側は諦めたと考えてよいだろう。すばらしい。
<結果>
クライアント側も、サーバ側も、お互いに処理を実行していないと判断できるため、状態の不整合は発生しない。すばらしい。
検討/実験(2−B)
処理遅延が発生したケース。
<クライアント側の状況>
- 接続(00): クライアント#connect -> OK
- 確認(00): クライアント#recv -> OK
- 要求(00): クライアント#send -> OK
- 結果(05): クライアント#recv -> Timeoutエラー
- 切断(05): クライアント#close -> OK
要求は送信できたが、結果受信をタイムアウトしてしまった。
<サーバ側の状況>
- 接続(00): サーバ#accept -> OK
- 確認(00): サーバ#send -> OK
- 要求(00): サーバ#recv -> OK
- 結果(10): サーバ#send -> OK
- 切断(10): サーバ#close -> OK
要求を受信できなかったし、結果を返すこともできたので、きっと、クライアントは正常に処理できたのだろう。すばらしい。
<結果>
クライアントは処理を失敗として扱い、サーバ側は処理を成功として暑かった。結果、クライアントとサーバ間では、状態の不整合が発生する。これでは、よろしくない。
プロトコル(3)
- 設定: クライアント#Linger(true, 0s)
- 接続: クライアント#connect -> サーバ#accept
- 確認: クライアント#recv <- サーバ#send
- 要求: クライアント#send -> サーバ#recv
- 結果: クライアント#recv <- サーバ#send
- 切断: クライアント#close , サーバ#close
検討/実験(3−A)
backlogに滞留しているケース。
<クライアント側の状況>
- 設定(00): クライアント#Linger(true, 0) -> OK
- 接続(00): クライアント#connect -> OK
- 確認(05): クライアント#recv -> Timeoutエラー
- 要求(--): クライアント#send -> skip
- 結果(--): クライアント#recv -> skip
- 切断(05): クライアント#close -> OK
とりあえず、要求を送信していないので、サーバ側で処理していないということは確実に判定できる。すばらしい。
<サーバ側の状況>
- 接続(10): サーバ#accept -> OK
- 確認(10): サーバ#send -> 即時エラー
- 要求(10): サーバ#recv -> skip
- 結果(--): サーバ#send -> skip
- 切断(10): サーバ#close -> OK
確認の送信に失敗したので、とりあえず、クライアント側は諦めたと考えてよいだろう。すばらしい。
<結果>
クライアント側も、サーバ側も、お互いに処理を実行していないと判断できるため、状態の不整合は発生しない。すばらしい。
検討/実験(3−B)
処理遅延が発生したケース。
<クライアント側の状況>
- 設定(00): クライアント#Linger(true, 0) -> OK
- 接続(00): クライアント#connect -> OK
- 確認(00): クライアント#recv -> OK
- 要求(00): クライアント#send -> OK
- 結果(05): クライアント#recv -> Timeoutエラー
- 切断(05): クライアント#close -> OK
要求は送信できたが、結果受信をタイムアウトしてしまった。
<サーバ側の状況>
- 接続(00): サーバ#accept -> OK
- 確認(00): サーバ#send -> OK
- 要求(00): サーバ#recv -> OK
- 結果(10): サーバ#send -> 即時エラー
- 切断(10): サーバ#close -> OK
結果の送信に失敗したので、とりあえず、クライアント側は諦めたと考えてよいだろう。だが、処理は行ってしまった。
<結果>
クライアント側はサーバ側にて処理が行われたかどうかは分からないものの、サーバ側もクライアントに結果を通知できなかったため、お互いが依頼をなかったことに(ロールバック)できれば、状態に相違は発生しない。まあ、良いかな。