今回は、JBOSS Nettyを使って、HTTPストリーミングを実現させる方法を紹介します。

HTTPストリーミングとはなんぞや?
ということですが、要はレスポンスが何度も分割されて出力されるだけです。
chunkedという形式で結果をかえせば、それがストリーミングという訳です。

たとえば、どんなところで使えるかと言えば、
チャットシステムなんかを構築する際に、受けるデータ(他人のコメント)を、
HTTPストリーミングという形で受ければ、つまり、PUSH配信ということが実現できるという事です。

どうやら、私はやらないし、つかわないのでよくわかりませんが、
twitterのStreaming APIみたいな感じ(まちがっていたらすいません。)のPUSH配信を実現する感じです。

では、nettyでのhandlerのサンプルを乗せますと、

package com.coltware.chat;

import java.util.Set;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpChunk;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ChatHandler extends SimpleChannelUpstreamHandler {
	
	private static final Logger log = LoggerFactory.getLogger(ChatHandler.class);
	
	public ChatHandler(){
		
	}

	@Override
	public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
			throws Exception {
		log.debug("channelOpen");
		super.channelOpen(ctx, e);
	}

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
			throws Exception {
		Object ret = e.getMessage();
		
		if(ret instanceof HttpRequest){
			final HttpRequest request = (HttpRequest)ret;
			Set<string> headers = request.getHeaderNames();
			for(String name:headers){
				log.debug("header[" + name + "]=>[" + request.getHeader(name) + "]");
			}
			
			HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
			response.setChunked(true);
			response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream");
			response.setHeader(HttpHeaders.Names.TRANSFER_ENCODING,HttpHeaders.Values.CHUNKED);
			
			log.debug("response" + response);
			
			e.getChannel().write(response);
			
			
			this.writeStringChunk(e.getChannel(), "this is 1st chunk data");
			this.writeStringChunk(e.getChannel(), "this is 2nd chunk data");
			
			e.getChannel().write(HttpChunk.LAST_CHUNK).addListener(ChannelFutureListener.CLOSE);
					
		}
		else{
			super.messageReceived(ctx, e);
		}
	}
	
	private void writeStringChunk(Channel channel,String data){
		ChannelBuffer chunkContent = ChannelBuffers.dynamicBuffer(channel.getConfig().getBufferFactory());
		chunkContent.writeBytes(data.getBytes());
		HttpChunk chunk = new DefaultHttpChunk(chunkContent);
		
		channel.write(chunk);
	}
}

これをサーバとして動かし、実際にHTTPを使ってアクセスすると

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Transfer-Encoding: chunked

16
this is 1st chunk data
16
this is 2nd chunk data
0

とこんな感じの結果になります。
この例では、1つの処理の中で2つのHttpChunkを返して、そのまま閉じてしまいましたが、
たとえば、Chatなどのシステムを作る場合には、
接続を閉じず(63行目を実行せず)、そのまま、全体の接続を他のところでとっておき、
それぞれに、このChunkでくぎられたデータを出力していけば、チャットシステムの機能が簡単に作れるという訳です。

ただし、これをActionScriptのURLStreamをつかってChunkごとにデータをキチンととれないかと、
調べてみましたが、どうやらそれができないようです。
chunkedの形式ではなく、
this is 1st chunk datathis is 2nd chunk data
のようにしかデータが取得できないので、データ自体に1つのデータの区切りを作るための仕掛けを作っておいた方が良さそうです。

お仕事のご依頼・相談を承ります
この記事に関連するお仕事のご依頼やご相談をお待ちしております。 詳しくは、こちら

Leave a Reply

お仕事のご依頼・相談
この記事に関連するお仕事のご依頼やご相談をお待ちしております。 詳しくは、こちら
ソフトウェア&ライブラリ




ライブラリ
airxmail(en)
AIR版メール送受信ライブラリ
airxzip
AIR版ZIP圧縮・解凍ライブラリ
執筆書籍
本、雑誌等

WEB記事:CodeZine
執筆記事はこちら
カレンダー
2011年12月
« 11月   1月 »
 1234
567891011
12131415161718
19202122232425
262728293031  

カスタム検索
RSS
Add to Google < !–adsense–>
アーカイブ
カテゴリ
にほんブログ村 IT技術ブログへ