Friday, November 02, 2007

XMLSocket(Flex) กับ Erlang

protocol ของ XMLSocket กำหนดไว้ว่า ข้อมูลที่ส่งแต่ละชุดจะปิดท้ายด้วย Zero Byte
นี่คือ code ง่ายๆของ Erlang ที่เปิดรับข้อมูลที่ส่งจาก XMLSocket
%% echo server แบบง่ายๆ
start() ->
{ok, Listen} = gen_tcp:listen(4114, [list,
{reuseaddr, true},
{active, true}]),
{ok, Socket} = gen_tcp:accept(Listen),
gen_tcp:close(Listen),
loop(Socket).

loop(Socket) ->
receive
{tcp, Socket, Data} ->
%% ก่อน parse ก็ให้ตัด byte สุดท้ายที่เป็น zero byte ออกก่อน
Txt = xmlutil:parse(strip(Data, right, $\0)),
%% ตอนส่งกลับก็ให้ต่อท้ายด้วย zero byte กลับไปด้วย
gen_tcp:send(Socket, concat(xmlutil:encode(concat(Txt, " pok")), [0])),
loop(Socket);
{tcp_closed, Socket} ->
io:format("Socket closed~n")
end.


ตัว XMLSocket ถึงจะตั้งชื่อให้มี XML นำหน้า แต่จริงๆแล้วมันก็เหมือน XMLHttpRequest ของ javascript
ที่ใช้ส่งข้อมูลอะไรก็ได้ ตามแต่ใจเรา
อย่างที่ผมเอามาใช้ แทนที่จะส่งข้อมูลมาให้ erlang ในรูปแบบ XML (ซึ่งเราขี้เกียจ parse)
ก็ให้ส่งมาเป็น erlang tuple เลย จะได้เอาไป execute ต่อได้ง่ายๆ

ใน erlang, มันมีวิธี eval String ง่ายๆดังนี้
eval(S,Environ) ->
{ok,Scanned,_} = erl_scan:string(S),
{ok,Parsed} = erl_parse:parse_exprs(Scanned),
erl_eval:exprs(Parsed,Environ).

ลอง run ดู
2> simple_server:eval("X=1+2.",[]).
{value,3,[{'X',3}]}
3> simple_server:eval("{plus,1,2}.",[]).
{value,{plus,1,2},[]}


เวลาเราเอาไปใช้กับ XMLSocket ก็จะเขียนประมาณนี้
loop(Socket) ->
receive
{tcp, Socket, Data} ->
Str = strip(Data,right,$\0),
{value, Cmd, _} = eval(Str,[]),
Result = execute(Cmd),
gen_tcp:send(Socket, concat(Result, [0])),
loop(Socket);
{tcp_closed, Socket} ->
io:format("Socket closed~n")
end.

execute({hello}) -> "world";
execute({who}) -> "pok";
execute({plus, X, Y}) -> integer_to_list(X+Y).


Update:
กรณีถ้าต้องการ parse จาก String เป็น Term
ใช้ code แค่นี้ก็พอ
eval(S) ->
{ok,Scanned,_} = erl_scan:string(S),
{ok,Term} = erl_parse:parse_term(Scanned),
Term.

Related link from Roti

No comments: