HTTPでサーバ側からデータをプッシュするには
休みなのにまじめな話題です。
HTTPでサーバ側からデータをプッシュしたいときにどうするか、というのを検討していました。HTTPは必ずクライアント側からリクエストを投げ、サーバはそれに応答しますが、サーバ側が送りたいときにデータを送る仕組みは基本的にありません。
すぐに思いつくのは、クライアントから定期的に「何かありませんか?」とリクエストを発行する方法(ポーリング)ですが、頻度が高いと無駄にネットワークトラフィックを消費してしまいます。
クライアントからリクエストを投げて、サーバが送りたいタイミングまで応答しない、という作戦も考えられますが、何も応答しないでいるとクライアント側がタイムアウトしてエラーになってしまいます。
一番それらしい解決方法は、クライアントからリクエストを投げて、サーバは「ちまちま」応答する方法です。ちまちま応答する方法で一番まともそうなのは、チャンク形式で応答する方法です。クライアントがタイムアウトしない程度に、何も送りたいデータがないときには、「何もない」というデータを決めて定期的に送り、送りたいデータがあるときだけ、そのデータを送れば良いのです。チャンク形式では最後にゼロというサイズを渡すまでは、クライアント側は受信を続けますので、接続を維持したまま、サーバが送りたいときだけデータを送る目的を果たせます。ひょっとするとプロキシがいたりすると、片方向だけの通信だと切られてしまうかも知れませんが、たまに切れるくらいなら再接続の仕組みを入れておけば良いでしょう。
実際に実験してみても、ずっと接続が切れることなく、サーバからクライアントにデータを渡し続けることができます。
この手法は「ロングポーリング」と呼ばれているらしく、さがせば多少は情報があります。
問題としては、接続を維持するので、サーバ側はずっとコネクションを張りっぱなしのクライアントの相手を多数しなければならないことで、さらに、CGIなどの場合は、CGIプロセスも起動しっぱなしになります。
私がさらにやりたかったのは、同じコネクションを使ってクライアント側からも送りたいときにデータを送ることができるか、ということです。クライアントからサーバにチャンク形式でリクエストを送り、普段は「何もない」というデータを送り、必要な時だけデータを送ることができます。CGIにはリアルタイムにデータが渡ります。サーバ側からも前述のように必要な時だけデータを送る方法と組み合わせれば、一つのコネクションで双方向、リアルタイムにデータをやりとりできる、と考えたのですが・・・
クライアント側からチャンク形式でデータを送ると、CGIにはリアルタイムでデータが渡るのですが、CGIから細切れでデータを応答しても、HTTPサーバはバッファリングしてしまいます。クライアントからのリクエストを完全に受信し終えないとサーバは応答を返さないのです。これはまあ、普通のHTTPを考えれば当然で、リクエストが不完全かも知れない時点で応答を返すのはおかしいということなのでしょう。
HTTPサーバを自作すれば、目的の動きはもちろんできるのですが、Apacheを使う限りは駄目そうです。送信と受信を別のコネクションにすればもちろん双方向リアルタイム通信を実現できますが、コネクション数が倍になってしまいます。
なお、Apacheとクライアントが直結の場合、クライアントからチャンクでちまちま送信していると大体1時間くらいでTCP接続が切られるみたいです。サーバからチャンクでちまちま送信している方はいつまででもTCP接続は切れないみたいです。プロキシが間に入っているとプロキシのポリシーで変わってくるでしょう。
そもそもHTTPで無理して双方向リアルタイム通信を作らなければいいじゃない、と言いたいところですが、企業間の通信はセキュリティ対策でファイアーウォールが存在し、HTTPは許可するが、他の特殊な通信は許可しない、というルールで運用している会社は多いのです。他にも、HTTPに対応した負荷分散装置などをそのまま使えるなどのメリットもあり、意地でもHTTPで構成したいというニーズは結構あるものなのです。
WEBシステムの開発は個人的にはあまり好きではないのですが、HTTPで何とかしなければというテーマは、ネットワークオタクとしては流すわけにはいかないので、久しぶりにCGIを作ったりしてみていますが、決められた仕組みの中で何とかするのは大変です。。