/project/pem/epem/src/_old2/daemon_server.erl

http://phidget-erlang-manager.googlecode.com/ · Erlang · 299 lines · 127 code · 68 blank · 104 comment · 0 complexity · 83e3fbe2d4224e132ace34a89dfc4c89 MD5 · raw file

  1. %% Author: Jean-Lou Dupont
  2. %% Created: 2009-06-23
  3. %% Description: Server side of the daemon
  4. %%
  5. %% This module:
  6. %% - Listens for incoming connections from Clients
  7. %% - Accepts 1 connection at a time
  8. %% - Receives messages from a Client
  9. %% - Send the messages to the Reflector
  10. %% {from_client, message, Msg}
  11. %%
  12. %% - Receives messages to be sent to Clients
  13. %% - Messages are received via the Reflector
  14. %%
  15. %% {to_client, {MsgId, Msg} }
  16. %% ^ ------------
  17. %% | ^
  18. %% MsgType |
  19. %% Msg
  20. %%
  21. %% - Receives messages from the socket
  22. %% These are sent to the Reflector with the following:
  23. %%
  24. %% {from_client, Msg}
  25. %%
  26. %% - Sends messages to clients
  27. %% - Sends message transmission status through Reflector
  28. %%
  29. %% {to_client_tx_status, {MsgId, Code} }
  30. %% Status Code = txerror || txok
  31. %%
  32. %% - Sends the Port opened for management Clients on the Reflector
  33. %%
  34. %% {management_port, Port}
  35. %% ^
  36. %% |
  37. %% MsgType
  38. -module(daemon_server).
  39. -define(ASSIGNEDPORT_TIMEOUT, 2000).
  40. %% Reflector subscriptions
  41. %% We just need to grab the messages destined
  42. %% to the Client side of the connection
  43. -define(SUBS, [to_client]).
  44. %%
  45. %% Exported Functions
  46. %%
  47. -export([
  48. start_link/0,
  49. start_link/1,
  50. stop/0,
  51. send_message/2
  52. ]).
  53. %%
  54. %% Local Functions
  55. %%
  56. -export([
  57. start_socket/1,
  58. loop_daemon/0,
  59. loop_socket/2,
  60. send_for_client/3,
  61. send_to_client/3
  62. ]).
  63. %% ======================================================================
  64. %% API Functions
  65. %% ======================================================================
  66. start_link() ->
  67. start_link([]).
  68. start_link(Args) ->
  69. Pid = spawn_link(?MODULE, loop_daemon, []),
  70. register(daemon_server, Pid),
  71. ?MODULE ! {args, Args},
  72. start_socket(0), %% pick a socket
  73. {ok, Pid}.
  74. stop() ->
  75. daemon_server ! stop.
  76. %% Send a message to the Client side
  77. %% of the connection
  78. send_message(MsgId, Msg) ->
  79. daemon_server ! {to_client, MsgId, Msg}.
  80. %% ======================================================================
  81. %% LOCAL Functions
  82. %% ======================================================================
  83. loop_daemon() -> %%daemon_server loop
  84. receive
  85. {args, Args} ->
  86. put(args, Args),
  87. switch:subscribe(daemon_server, ?SUBS);
  88. {switch, subscribed} ->
  89. %%base:ilog(?MODULE, "subscribed~n",[]),
  90. switch:publish(daemon_server, ready, self());
  91. stop ->
  92. exit(ok);
  93. %% Status of transmission to client side
  94. {tx_status, {info, Code, MsgId}} ->
  95. %%base:ilog(?MODULE, "tx_status: Code[~p] MsgId[~p]~n",[Code, MsgId]),
  96. switch:publish(?MODULE, to_client_tx_status, {MsgId, Code});
  97. %% The listen port could have been assigned
  98. %% by the OS: we need to keep track of it
  99. %% for managing the daemon through a management
  100. %% client interface.
  101. {assignedport, Port} ->
  102. put(assignedport, Port),
  103. switch:publish(?MODULE, management_port, Port);
  104. %% message to send down the socket ... if any.
  105. %% send to socket process for delivery
  106. {_From, to_client, {MsgId, Msg}} ->
  107. SocketPid = get(socket_pid),
  108. send_for_client(SocketPid, MsgId, Msg);
  109. %% Just acting as relay.
  110. %% This type of message comes from the socket process
  111. %% (from the Client side) and needs to be relayed.
  112. %% The message is sent on the Reflector.
  113. {from_client, Message} ->
  114. switch:publish(?MODULE, from_client, Message);
  115. %% Need to track the socket process Pid in order
  116. %% to establish a communication link between
  117. %% a Client and a resident daemon
  118. {daemon_socket_pid, Pid} ->
  119. put(socket_pid, Pid);
  120. %%base:ilog(?MODULE, "socket Pid[~p]~n", [Pid]);
  121. {closed, _Sock} ->
  122. ok;
  123. %%base:ilog(?MODULE,"Socket[~p] closed~n", [Sock]);
  124. {lsocket, _LSocket} ->
  125. ok;
  126. %%base:ilog(?MODULE,"LSocket[~p]~n", [LSocket]);
  127. {socket, _Socket} ->
  128. ok;
  129. %%base:ilog(?MODULE,"Socket[~p]~n", [Socket]);
  130. {error, lsocket, _Reason} ->
  131. ok;
  132. %%base:elog(?MODULE,"LSocket Error[~p]~n", [Reason]);
  133. {error, socket, _Reason} ->
  134. ok;
  135. %%base:elog(?MODULE,"Socket Error[~p]~n", [Reason])
  136. Other ->
  137. base:elog(?MODULE, "unhandled message [~p]~n", [Other])
  138. %% We always have to sync to the reflector;
  139. %% we can't rely on the having to send stuff
  140. %% to re-sync.
  141. after ?ASSIGNEDPORT_TIMEOUT ->
  142. Port=get(assignedport),
  143. switch:publish(?MODULE, management_port, Port)
  144. end,
  145. loop_daemon().
  146. %% CALLED FROM daemon_server PROCESS
  147. %% These functions server as bridge between the daemon_server
  148. %% process and the socket process
  149. send_for_client(undefined, MsgId, Msg) ->
  150. %% TODO throttle this?
  151. base:elog(?MODULE, "socket process ERROR, cannot send MsgId[~p] Msg[~p]~n", [MsgId, Msg]);
  152. send_for_client(SocketPid, MsgId, Msg) ->
  153. SocketPid ! {for_client, MsgId, Msg}.
  154. start_socket(Port) ->
  155. {Code, LSocket} = gen_tcp:listen(Port,[{nodelay, true}, {packet, 2}, {reuseaddr, true},{active, true}]),
  156. case Code of
  157. ok ->
  158. %% Retrieve the Port number as it could have been assigned by the OS
  159. %% by setting '0' in the call to gen_tcp:listen
  160. AssignedPort = inet:port(LSocket),
  161. daemon_server ! {lsocket, LSocket},
  162. Pid = spawn_link(?MODULE, loop_socket, [AssignedPort, LSocket]),
  163. register(daemon_socket, Pid),
  164. %% info back to the main loop 'daemon_server'
  165. {_, Porte} = AssignedPort,
  166. base:ilog(?MODULE, "assignedport [~p]~n",[Porte]),
  167. daemon_server ! {assignedport, Porte},
  168. daemon_server ! {daemon_socket_pid, Pid},
  169. %% Start accepting calls!
  170. daemon_socket ! {dostart};
  171. _ ->
  172. daemon_server ! {error, lsocket, LSocket}
  173. end,
  174. ok.
  175. %% =========================================================================================================================
  176. %% =========================================================================================================================
  177. %% Socket process loop
  178. %% =========================================================================================================================
  179. %% =========================================================================================================================
  180. loop_socket(Port, LSocket) ->
  181. receive
  182. %% waits for a connection i.e. BLOCKING <=======================================
  183. %% =============================================================================
  184. {dostart} ->
  185. {Code, Socket} = gen_tcp:accept(LSocket),
  186. inet:setopts(Socket, [{packet,2},binary,{nodelay, true},{active, true}]),
  187. case Code of
  188. ok ->
  189. put(socket, Socket),
  190. daemon_server ! {socket, Socket};
  191. _ ->
  192. daemon_server ! {error, socket, Socket},
  193. %% try again
  194. %% TODO think about limiting this?
  195. self() ! {dostart}
  196. end;
  197. %% Message Reception
  198. %% Rx from Client connected on the socket,
  199. %% message is relayed through the daemon_server.
  200. {tcp, Sock, Data} ->
  201. Message = binary_to_term(Data),
  202. daemon_server ! {from_client, Message},
  203. base:ilog(?MODULE, "tcp socket: Sock[~p] Decoded[~p]~n", [Sock, Message]);
  204. %% Client Connection close... restart
  205. {tcp_closed, Sock} ->
  206. daemon_server ! {closed, Sock},
  207. self() ! {dostart};
  208. %% Message to deliver to the Client connected to the socket
  209. {for_client, MsgId, Msg} ->
  210. Socket=get(socket),
  211. send_to_client(Socket, MsgId, Msg);
  212. Other ->
  213. base:elog(?MODULE, "socket: Other[~p]~n", [Other])
  214. end, %%RECEIVE
  215. loop_socket(Port, LSocket).
  216. %% CALLED BY SOCKET PROCESS
  217. %% Send message down the socket to the Client side
  218. send_to_client(undefined, MsgId, Msg) ->
  219. %% TODO throttle?
  220. base:elog(?MODULE, "socket error, cannot send, MsgId[~p] Msg[~p]~n", [MsgId, Msg]),
  221. daemon_server ! {tx_status, {info, txerror, MsgId}};
  222. send_to_client(Socket, MsgId, Msg) ->
  223. base:elog(?MODULE, "Message to client, MsgId[~p] Msg[~p]~n", [MsgId, Msg]),
  224. Coded = term_to_binary(Msg),
  225. case gen_tcp:send(Socket, Coded) of
  226. ok ->
  227. daemon_server ! {tx_status, {info, txok, MsgId}};
  228. {error, Reason} ->
  229. base:elog(?MODULE, "send_to_client: ERROR, Reason[~p] MsgId[~p] Msg[~p]~n", [Reason, MsgId, Msg]),
  230. daemon_server ! {tx_status, {info, txerror, MsgId}}
  231. end.