练习写个SGS的flash socket策略服务器

    http://duker.iteye.com/blog/209040上面提到flash socket的策略服务器问题如何用MINA2来解决。另外,在http://code.google.com/p/hudo/上的hudo as3 网络通信框架也提出使用一个简单的java程序监听843端口。网上介绍的解决方法很多,例如http://bbs.9ria.com/viewthread.php?tid=15182

    这个问题源于Actionscript3网络通信的特殊性——在通信之前必须(哪怕你代码里没有明显的程序表现)进行一次的策略请求。类似于html协议的策略文件和js的策略文件,只不过用tcp协议。SGS是开源的网游服务器引擎,但没有解决此问题的直接方法,我觉得用mina1.1.7解决这个问题比较方便,就试着写写,觉得还可行(至少hudo的demo可以用这种方法)。

    我的思路是,添加一个SGS服务,然后开mina服务器监听843端口,如果有合适的输入就返回crossdomain.xml开放端口。

    声明几点,一,我开一个SGS所以不会出现843端口冲突问题,二,我不知道代码是否经得起高并发——没有跨域策略文件,flash将无法进行任何socket连接,那是非常致命的问题。

    用hudo的demo来说:

    首先,修改SGS的配置文件conf\sgsApp.properties

# This is the properties file for running the MUDServer application

com.sun.sgs.app.name=thServer
com.sun.sgs.app.root=data/thServer
com.sun.sgs.app.port=23
com.sun.sgs.impl.transport.tcp.listen.port=23
com.sun.sgs.app.listener=thgame.GameChannels


# SGS 0.9.5.1-r3730
# see com.sun.sgs.impl.kernel.ServiceConfigRunner:fetchServices()
# My own service , use ":" to split
#com.sun.sgs.app.services=thgame.FlashPolicyServiceImpl
# Managers may be "", but CANNOT delete this line
#com.sun.sgs.app.managers=

# SGS 0.9.9
# see com.sun.sgs.impl.kernel.Kernel:fetchServices()
# My own service , use ":" to split
com.sun.sgs.services=thgame.FlashPolicyServiceImpl
# Managers may be "", but CANNOT delete this line
com.sun.sgs.managers=

 

注意,0.9.5和0.9.9不同,com.sun.sgs.app.* 和 com.sun.sgs.*,而且managers都不可以省略,详细可以参考SGS源代码。然后就是写那个thgame.FlashPolicyServiceImpl类了

 

package thgame;

import com.sun.sgs.kernel.ComponentRegistry;
import com.sun.sgs.service.Service;
import com.sun.sgs.service.TransactionProxy;
import java.util.Properties;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.common.DefaultIoFilterChainBuilder;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.IoAcceptorConfig;
import org.apache.mina.filter.LoggingFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;

/**
 * Simple Service implementation. <p> 
 */
public class FlashPolicyServiceImpl implements Service {
    /** Choose your favorite port number. */
    private static final int PORT = 843;
    

        
    public FlashPolicyServiceImpl(Properties properties,
		      ComponentRegistry systemRegistry,
		      TransactionProxy txnProxy)
			throws Exception
	{
    	System.out.println("FlashPolicyServiceImpl construct");
    	
    	//退出函数后线程仍存在
    	IoAcceptor acceptor = new SocketAcceptor();
    	IoAcceptorConfig config = new SocketAcceptorConfig();
    	DefaultIoFilterChainBuilder chain;

        chain = config.getFilterChain();
        
    	/**
    	 * 启用/禁用 SO_REUSEADDR 套接字选项。
    	 * 超过一个的套接字绑定到相同的套接字地址。
    	 * 如果此功能不受支持,则 getReuseAddress() 将始终返回 false
    	 */
    	//((SocketAcceptorConfig)config).setReuseAddress(true);
    	//System.out.println("getReuseAddress:" + ((SocketAcceptorConfig)config).isReuseAddress());
    	/**
    	 * 重复加logger会抛异常
    	 */
    	//chain.addLast("logger", new LoggingFilter());
        
        /**
         * 文本模式
         */
        //chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));
        
        // Bind
        try {
			acceptor.bind(new InetSocketAddress(PORT), new FlashPolicyProtocolHandler(), config);
			System.out.println("FlashPolicyServiceImpl Listening on port " + PORT);    
        } catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
        
    	return;
	}
    
    public String getName() {
    	System.out.println("FlashPolicyServiceImpl ready");
    	
    	return toString();
    }

    public void ready() { 
    	
        return;
    }

    public void shutdown() {
    	System.out.println("FlashPolicyServiceImpl shutdown");
    }
}

 

再加上最为重要的handler就大功告成

package thgame;

import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

import org.apache.mina.common.IoHandler;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.ByteBuffer;


public class FlashPolicyProtocolHandler extends IoHandlerAdapter {	
    private static String POLICY_FILE = "<cross-domain-policy>" + 
    	"<site-control permitted-cross-domain-policies=\"all\"/>" +
    	"<allow-access-from domain=\"*\" to-ports=\"*\" />" +
    	"</cross-domain-policy>";	
    
	private Charset ch = Charset.forName("utf-8");     
	private CharsetDecoder decoder = ch.newDecoder();  
	private CharsetEncoder encoder = ch.newEncoder();
    
    public void exceptionCaught(IoSession session, Throwable cause) {
        //cause.printStackTrace();
        // Close connection when unexpected exception is caught.
        session.close();
    }
   
	@Override
    public void messageReceived(IoSession session, Object message) {
        try {
			//System.out.println("received...");
			if(! (message instanceof ByteBuffer)) {
				session.close();
				return ;
			}
			
			ByteBuffer rb = (ByteBuffer) message;
			String str = rb.getString(decoder);
			
			//System.out.println(str);
			
			if(str.equals("<policy-file-request/>")){
	        	//System.out.println("<policy-file-request/>");
	        	ByteBuffer wb = ByteBuffer.allocate(POLICY_FILE.length());
				wb.putString(POLICY_FILE, encoder);
	            /**
	             * ByteBuffer必须flip
	             */
				wb.flip();
	            session.write(wb);
	        }
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			session.close();
		}
    }
}
	

 

个人认为flash虽然对策略文件很重视,但通常只会在通信前读一次,以后不会再读,所以对服务器性能影响不会太大。

 

----------------------------------------------

 

补注:

由于这个问题是SGS上(试验性质)。

真实世界中,有人遇到端口连接过多而无法读出策略xml的情况(在广州交流会上听到)

所以这个问题在高并发情况下还挺复杂,并不是简单写个adapter类就能解决。

 

 

 

---------------------------------------------------

 

20100927补注:

除了策略文件外,想起来还遗漏掉flash开发中一个非常重要的问题,就是本地信任:

http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html

 

如果使用fd的话,还会出现一种情况,你直接打开是本地文件不信任,但用FD会本地信任,而且还会影响到直接打开——以后打开swf也变成本地信任。原因是有个配置文件在起作用,它记录下曾经用FD打开过的swf,致使它们本地信任(可以访问本地文件)

假设使用Administrator用户,则在Windows XP的目录

C:\Documents and Settings\Administrator\Application Data\Macromedia\Flash Player\#Security\FlashPlayerTrust

的xxx.cfg就是造成本地信任的原因!

如果想模拟真实的用户系统(没有调试过flash),必须把此目录下的文件全部删除!否则不信任对话框不弹出!

详细请参考:

http://www.cc-space.com/?p=401

 

 

 

 

 ------------------------------------------------------

 

 

assql有更详细的关于策略服务器的搭建方法(使用各种编程语言),可以参考:

http://code.google.com/p/assql/wiki/SecurityInformation

 

另外socket的策略文件除了可以在843端口读,还可以在连接中的端口读取,可以根据项目需要选择。

 

 

 

--------------------------------------------------------

 

mina2以后的线程池可能需要手工关闭(或者用System.exit(0))

下面代码仅供参考

 

 

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;

import org.apache.mina.core.service.SimpleIoProcessorPool;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioProcessor;
import org.apache.mina.transport.socket.nio.NioSession;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

...

/**
 * 服务器实例
 * @author Administrator
 *
 */
public final class PackServer {
    private static final Logger LOG = Logger.getLogger(PackServer.class);
    private static ExecutorService executorService = 
	Executors.newCachedThreadPool();
    private static SimpleIoProcessorPool<NioSession> pool = 
	new SimpleIoProcessorPool<NioSession>(
	    NioProcessor.class, 
	    executorService, 
	    Runtime.getRuntime().availableProcessors() + 1);

    private static NioSocketAcceptor acceptor = new NioSocketAcceptor(pool);

    public static void startSever() throws IOException {
	acceptor.setHandler(new PackServerHandler());
	acceptor.setReuseAddress(true);
	acceptor.bind(new InetSocketAddress(GlobalConfig.DEFAULT_PORT));
	LOG.info("Listening on port " + GlobalConfig.DEFAULT_PORT);
    }

    public static void stopServer() {
	if (acceptor != null) {
	    //关闭线程池,否则无法退出
	    executorService.shutdown();
	    //关闭服务器路由总开关
	    acceptor.unbind();
	    acceptor.dispose();
	}
    }

    // ---------------------------------------------
    private PackServer() {

    }
}

 

-----------------------------------------------

 

这里有个jboss netty版的flash策略服务器实现:

 

https://github.com/cgbystrom/netty-tools/blob/master/src/main/java/se/cgbystrom/netty/FlashPolicyHandler.java

 

 

 

 

 

 

你可能感兴趣的:(apache,socket,Flash,Mina,sun)