/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.convertprocess.server; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.log4j.Logger; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineCoverage; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import com.ttProject.util.BufferUtil; /** * 標準入力としてデータをffmpeg等の外部プロセスに渡すために利用するサーバー動作 * @author taktod */ public class ProcessServer { /** ロガー */ private Logger logger = Logger.getLogger(ProcessServer.class); /** つながっているクライアントのchannelデータ */ private final Set<Channel> channels = new HashSet<Channel>(); /** 動作サーバーチャンネル */ private final Channel serverChannel; /** サーバー用の動作bootstrap */ private final ServerBootstrap bootstrap; /** * コンストラクタ * @param port */ public ProcessServer(int port) { ExecutorService executor = Executors.newCachedThreadPool(); bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory(executor, executor)); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("handler", new ProcessServerHandler()); return pipeline; } }); serverChannel = bootstrap.bind(new InetSocketAddress(port)); } /** * データを送る * @param buffer */ public void sendData(ByteBuffer buffer) { // 先頭にsize情報をつけておかないとclient側でどこまでが一まとまりかがわからなくなります。 ByteBuffer size = ByteBuffer.allocate(4); size.putInt(buffer.remaining()); size.flip(); ChannelBuffer sendBuffer = ChannelBuffers.copiedBuffer(BufferUtil.connect(size, buffer)); // データのサイズを先行して設定しないとだめです。 synchronized(channels) { for(Channel channel : channels) { channel.write(sendBuffer); // futureをつかって待ってもだめっぽいね、別threadで転送を実行しておこうと思う。 } } } /** * サーバーを閉じます */ public void closeServer() { synchronized(channels) { for(Channel channel : channels) { channel.close(); } channels.clear(); } ChannelFuture future = serverChannel.close(); future.awaitUninterruptibly(); bootstrap.releaseExternalResources(); } /** * 処理クラス * @author taktod */ @ChannelPipelineCoverage("one") private class ProcessServerHandler extends SimpleChannelUpstreamHandler { /** * 例外取得時 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { // super.exceptionCaught(ctx, e); } /** * 接続したときの動作 * channelオブジェクトを保持しておく * @param ctx * @param e * @throws Exception */ @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.info("接続してきた。"); synchronized (channels) { channels.add(e.getChannel()); } } /* @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { synchronized (channels) { channels.add(e.getChannel()); } }*/ /** * 終了時 */ @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.info("閉じました"); synchronized (channels) { channels.remove(e.getChannel()); } } /** * 切断時 */ @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.info("切断しました。"); synchronized (channels) { channels.remove(e.getChannel()); } } } }