Erlang与Flash的Socket通讯-02

分享ErlangFlashSocketTCP by 达达 at 2010-08-11

前一次实验,我使用的是本地swf文件,所以没有遇到安全沙箱问题,但在实际环境中我们的Flash文件是通过URL引用的,这时候Flash的Socket通讯会受到安全沙箱机制的限制。具体原理是:Flash在尝试向服务器A发起Socket连接前,会先尝试连接服务器A的843端口,求情一个xml格式的安全策略文件,该文件告诉Flash是否有权限向服务器A发起Socket连接。所以,我们需要在Erlang端增设一个守候在843端口的安全策略文件服务器。

此次实验的Erlang代码:

-module(server).
-export([start/1]).
-export([policy_file_server/0, policy_file_server_proc/1]).
-export([flash_socket_server/1, flash_socket_server_proc/1]).

start (Port) ->
    spawn(?MODULE, policy_file_server, []),
    spawn(?MODULE, flash_socket_server, [Port]).


%%------------------------------------------------------------------------------
%% Flash Policy File Server
%%------------------------------------------------------------------------------

policy_file_server () ->
    {ok, LSock} = gen_tcp:listen(843, [binary, {packet, 0}, {active, false}]),

    policy_file_server_accept(LSock).

policy_file_server_accept (LSock) ->
    case gen_tcp:accept(LSock) of
        {ok, Sock} ->
            spawn(?MODULE, policy_file_server_proc, [Sock]),

            policy_file_server_accept(LSock);

        {error, Reason} ->
            io:format("policy file server exit: ~s~n", [Reason]),
            exit(Reason)
    end.

policy_file_server_proc (Sock) ->
    case gen_tcp:recv(Sock, 0) of
        {ok, _} ->
            gen_tcp:send(Sock, <<
                "<?xml version="1.0"?>"
                "<cross-domain-policy>",
                "<allow-access-from domain="*" to-ports="*" />",
                "</cross-domain-policy>",0
            >>);
        _ ->
            ok
    end,
    gen_tcp:close(Sock).

%%------------------------------------------------------------------------------
%% Flash Socket Server
%%------------------------------------------------------------------------------

flash_socket_server (Port) ->
    {ok, LSock} = gen_tcp:listen(Port, [list, {packet, 2}, {active, false}]),

    flash_socket_server_accpet(LSock).

flash_socket_server_accpet (LSock) ->
    case gen_tcp:accept(LSock) of
        {ok, Sock} ->
            spawn(?MODULE, flash_socket_server_proc, [Sock]),

            flash_socket_server_accpet(LSock);

        {error, Reason} ->
            io:format("flash socket server exit: ~s~n", [Reason]),
            exit(Reason)
    end.

flash_socket_server_proc (Sock) ->
    case gen_tcp:recv(Sock, 0) of
        {ok, Request} ->
            io:format("Received: ~p~n", [Request]),

            gen_tcp:send(Sock, "done!"),

            flash_socket_server_proc(Sock);

        {error, closed} ->
            io:format("Client closed~n"),
            exit(closed);

        {error, Reason} ->
            io:format("Error: ~s~n", Reason),
            exit(Reason)
    end.

在调试Flash端时,我调试了很久都没有效果,最后发现原来是浏览器缓存问题。

以下是Flash端的实验代码:

package
{
    import flash.events.MouseEvent;
    import flash.events.ProgressEvent;
    import flash.net.Socket;
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;

    public class Main extends Sprite
    {
        private var sock:Socket;

        private var send:TextField;
        private var input:TextField;
        private var output:TextField;

        public function Main ()
        {
            send = new TextField();
            send.border = true;
            send.multiline = true;
            send.autoSize = TextFieldAutoSize.CENTER;
            send.htmlText = "<a href='event:#'>   Send   </a>";
            send.mouseEnabled = true;
            send.selectable = false;

            send.x = 320 - send.width;
            send.y = 20;
            send.height = 21;

            input = new TextField();
            input.border = true;
            input.type = "input";
            input.autoSize = TextFieldAutoSize.NONE;

            input.x = 20;
            input.y = 20;
            input.width = 300 - send.width - 10;
            input.height = 19;

            output = new TextField();
            output.border = true;
            output.multiline = true;
            output.autoSize = TextFieldAutoSize.NONE;

            output.x = 20;
            output.y = 20 + input.height + 10;
            output.width = 300;
            output.height = 300;
            output.wordWrap = true;

            addChild(send);
            addChild(input);
            addChild(output);

            sock = new Socket();

            sock.addEventListener(
                ProgressEvent.SOCKET_DATA,
                function (event:ProgressEvent):void
                {
                    /* 与Erlang端{packet, 0}对应的代码
                while (sock.bytesAvailable)
                    {
                        var s:String = sock.readUTFBytes(sock.bytesAvailable);

                        output.appendText(s);
                    }
                    */

                    /* 与Erlang端{packet, 0}对应的代码 */
                    var s:String = sock.readUTF();

                    input.text = "";

                    output.appendText(s);
                    output.appendText("rn");
                }
            );

            sock.connect("localhost", 10086);

            sock.flush();

            send.addEventListener(
                MouseEvent.CLICK,
                function (event:MouseEvent):void
                {
                    /* 与Erlang端{packet, 0}对应的代码
                    sock.writeUTFBytes("Hello World!");
                    */

                    /* 与Erlang端{packet, 0}对应的代码 */
                    sock.writeUTF(input.text);

                    sock.flush();
                }
            );
        }
    }
}