免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 9979 | 回复: 4
打印 上一主题 下一主题

[其他] 发布一个erlang服务端,flash客户端的聊天服务(带安全沙箱解决)[更新] [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-12-10 16:27 |只看该作者 |倒序浏览
erlang服务端,主要实现与flash客户端的连接、消息传递服务
自带843端口服务解决flash9以上暴汗的安全沙箱问题

chat.erl

  1. -module(chat).
  2. -export([start/0,manage_clients/1,stop/0,find/1]).
  3. -record(player, {id,socket,time=none}).

  4. start() ->
  5.         chat_server:start(?MODULE,manage_clients).

  6. stop()->
  7.         chat_server:stop(?MODULE),
  8.         timer:sleep(2000),                                                %%等待所有连接断开
  9.         manage_clients!{exit}.

  10. manage_clients(Players) ->
  11.         receive
  12.             {connect, Socket} ->
  13.                 Player = #player{
  14.                         id="guest_"++integer_to_list(random:uniform(100000)),
  15.                         socket=Socket,
  16.                         time=time()
  17.                         },        %%新增记录
  18.                 io:fwrite("Socket connected: ~w~n", [Socket]),
  19.                 NewPlayers =  [Player|Players];                                %%累加到列表

  20.             {disconnect, Socket} ->
  21.                 io:fwrite("Socket disconnected: ~w~n", [Socket]),
  22.                 Player = find_Socket(Socket, Players),                        %%查找socket对应的记录
  23.                 NewPlayers = lists:delete(Player, Players);                %%删除记录

  24.             {chat,Data} ->
  25.                 send_data(Players, Data),
  26.                 NewPlayers=Players;

  27.             {list,Uname}->
  28.                 find_player(Uname,Players),
  29.                 NewPlayers = Players;

  30.             {exit}->
  31.                 NewPlayers="",
  32.                 exit({stop, exit})                                       
  33.         end,
  34.         manage_clients(NewPlayers).





  35. %%对客户端进行广播
  36. send_data(Players, Data) ->
  37.   SendData = fun(Player) ->
  38.     gen_tcp:send(Player#player.socket, Data)                         
  39.   end,
  40.   lists:foreach(SendData, Players).

  41. %%查找指定记录
  42. find_Socket(Socket, Players) ->
  43.     {value, Player} = lists:keysearch(Socket, #player.socket, Players),
  44.     Player.

  45. find_player(Uname, Players) ->
  46.     case lists:keysearch(Uname, #player.id, Players) of
  47.     {value, Player}->
  48.         io:fwrite("Find a player: ~w~n", [Player]);
  49.     _->io:fwrite("No found.~n")
  50.     end.


  51. %%查找用户
  52. find(Uname)->
  53.         client_manager ! {list,Uname}.
复制代码


chat_server.erl

  1. -module(chat_server).
  2. -behaviour(gen_server).
  3. -export([start/2,stop/1]).

  4. %% gen_server回调
  5. -export([init/1,handle_call/3,handle_cast/2,handle_info/2,
  6.         terminate/2,code_change/3]).
  7. -compile(export_all).

  8. -define(TCP_OPTIONS, [list, {packet, 0}, {active, false}, {reuseaddr, true},{nodelay, false},{delay_send, true}]).  
  9. %%状态表
  10. -record(state, {name,loop,socket}).

  11. %%回调gen_server创建服务
  12. start(Name,Loop) ->
  13.         State=#state{name=Name,loop=Loop},                                %%定为记录
  14.         gen_server:start_link({local,Name},?MODULE,State,[]).                       


  15. %%start() ->
  16. %%        gen_server:start_link({local,?MODULE},?MODULE,[],[]).               


  17. %%停止服务
  18. stop(Name)  ->
  19.         gen_server:call(Name,stop).


  20. init(State) ->
  21.         %%维护队列 chat.rar (17.12 KB, 下载次数: 425)
  22.         register(State#state.loop, spawn(fun() -> (State#state.name):(State#state.loop)([]) end)),
  23.         {Tag, LSocket}=gen_tcp:listen(8080, ?TCP_OPTIONS),                        %%默认8080
  24.         case Tag of                                                                %%创建监听
  25.                 ok        ->        spawn(fun() -> do_accept(State#state{socket=LSocket}) end);        %%统一接收
  26.                 error        ->        exit({stop, exit})                                %%出错,退出连接
  27.         end,
  28.                                
  29.         %%与flash通信的安全策略文件传送监听接口
  30.         {Req, AsSocket}=gen_tcp:listen(843, ?TCP_OPTIONS),
  31.         case Req of                                                               
  32.                 ok        ->        spawn(fun() -> do_accept2(AsSocket) end);       
  33.                 error        ->        exit({stop, exit})                               
  34.         end,
  35.         {ok,LSocket}.

  36. handle_call(stop,_From,Tab)        ->
  37.         {stop,normal,stopped,Tab}.


  38. handle_cast(stop,State)                ->{stop, normal, State};
  39. handle_cast(_Msg,State)                ->{noreply,State}.
  40. handle_info(_Info,State)        ->{noreply,State}.
  41. terminate(_Reason,_State)        ->ok.
  42. code_change(_OldVsn,State,_)        ->{ok,State}.







  43. %% -----------------------------------
  44. %% -------------私有函数--------------
  45. %% -----------------------------------



  46. %%新建连接,只有新连接时才调用
  47. do_accept(State) ->
  48.         case gen_tcp:accept(State#state.socket) of
  49.                 {ok, Socket}->
  50.                         %%创建进程处理响应
  51.                         spawn(fun() -> handle_client(State,Socket) end),
  52.                         %%创建新连接,持久化
  53.                         State#state.loop ! {connect, Socket},
  54.                         do_accept(State);
  55.                 _->
  56.                         ok
  57.         end.       



  58. %%处理flash安全策略文件的发送服务
  59. do_accept2(LSocket) ->
  60.         case gen_tcp:accept(LSocket) of
  61.                 {ok, Socket}->
  62.                         spawn(fun() -> put_file(Socket) end),
  63.                         do_accept2(LSocket);
  64.                 _->ok
  65.         end.

  66. %%短连接策略,连接完关闭
  67. put_file(Socket)->
  68.           case gen_tcp:recv(Socket, 0) of
  69.                 {ok, _} ->
  70.                         %%判断数据返回策略文件
  71.                         gen_tcp:send(Socket, "<?xml version=\"1.0\"?>
  72.                 <cross-domain-policy>
  73.                 <allow-access-from domain=\"*\" to-ports=\"*\"/>  
  74.                 </cross-domain-policy>\0");
  75.                 {error, closed} ->ok
  76.           end,
  77.           gen_tcp:close(Socket).                                        %%关闭连接




  78. handle_client(State,Socket) ->
  79.           case gen_tcp:recv(Socket, 0) of
  80.                 {ok, Data} ->
  81.                         State#state.loop ! {chat, Data},
  82.                         handle_client(State,Socket);
  83.                 {error, closed} ->
  84.                         State#state.loop ! {disconnect, Socket}               
  85.           end.



复制代码






简单的flash客户端,为as3代码:

  1. package  
  2. {
  3.         import flash.net.Socket;
  4.         import flash.display.Sprite;
  5.         import flash.events.*;
  6.         import flash.text.TextField;

  7.         [SWF(width=600, height=300)]
  8.         public class chat extends Sprite
  9.         {
  10.                 private var textArea:TextField;
  11.                 private var input:TextField;
  12.                 private var label:TextField;
  13.                 private var socket:Socket=new Socket();       

  14.                 public function chat()
  15.                 {
  16.                         socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
  17.                         socket.connect("192.168.10.112", 8080);                                                                //连接到服务端,这里改成服务端的IP和端口
  18.                         socket.flush();

  19.                         initUI();
  20.                         input.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);                //按回车响应
  21.                         textArea.addEventListener(Event.RENDER, renderHandler);                       
  22.                 }
  23.                
  24.                 private function sendMsg(Msg:String):void
  25.                 {
  26.                         socket.writeUTFBytes(Msg);
  27.                         socket.flush();               
  28.                 }
  29.                
  30.                 private function keyDownHandler(e:KeyboardEvent):void
  31.                 {
  32.                         if (13 == e.charCode)
  33.                         {
  34.                                 var s:String = input.text ? input.text : "null";
  35.                                 input.text = "";
  36.                                 sendMsg(s);
  37.                         }
  38.                 }
  39.                
  40.                 private function renderHandler(e:Event):void
  41.                 {
  42.                         textArea.scrollV = textArea.maxScrollV;
  43.                 }
  44.                
  45.                
  46.                 //初始化
  47.                 private function initUI():void
  48.                 {
  49.                         textArea = new TextField();
  50.                         textArea.width = 400;
  51.                         textArea.height = 150;
  52.                         textArea.border = true;
  53.                         textArea.x = 100;
  54.                         textArea.y = 30;
  55.                         addChild(textArea);
  56.                        
  57.                         input = new TextField();
  58.                         input.height = 20;
  59.                         input.width = 350;
  60.                         input.type = "input";
  61.                         input.border = true;
  62.                         input.multiline = false;
  63.                         input.x = 100;
  64.                         input.y = 200;
  65.                         addChild(input);
  66.                        
  67.                         label = new TextField();
  68.                         label.text = "回车发送";
  69.                         label.height = label.textHeight + 4;
  70.                         label.width = label.textWidth + 4;
  71.                         label.mouseEnabled = false;
  72.                         label.x = 460;
  73.                         label.y = 200;
  74.                         addChild(label);
  75.                 }

  76.                
  77.                 // 事件处理回调函数,显示到屏幕上
  78.                 private function onSocketData(event:ProgressEvent):void
  79.                 {
  80.                         while (socket.bytesAvailable)
  81.                         {
  82.                                 var s:String = socket.readUTFBytes(socket.bytesAvailable);
  83.                                 if (textArea.text) s = "\n" + s;
  84.                                 textArea.appendText(s);                       
  85.                                 stage.invalidate();
  86.                         }
  87.                 }
  88.         }
  89. }
复制代码



运行:

1> c(chat).
{ok,chat}
2> c(chat_server).
{ok,chat_server}
3> chat:start().  
{ok,<0.435.0>}

.....
n> chat:stop().   
{exit}



更多部署见附件:

[ 本帖最后由 bs 于 2009-12-15 20:04 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2009-12-19 14:33 |只看该作者
多谢BS版主共享好东西啊。

论坛徽章:
0
3 [报告]
发表于 2009-12-31 23:37 |只看该作者
多谢版主.......

论坛徽章:
0
4 [报告]
发表于 2010-01-22 23:39 |只看该作者
没有AMF和RPC实用价值也不是太大

论坛徽章:
0
5 [报告]
发表于 2010-01-25 14:57 |只看该作者
为防止粘包

erlang 下设置
define(TCP_OPTIONS, [list, {packet, 2}, {active, false}, {reuseaddr, true},{nodelay, false},{delay_send, true}]).  

as下,使用
readUTF读取
writeUTF发送


不过,与as3安全策略通信时的843端口默认要设置成  {packet, 0} 否则很可能无法通信

[ 本帖最后由 bs 于 2010-1-25 15:04 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP