Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

support PUBLISH and SUBSCRIBE

  • Loading branch information...
commit 43d8ce198d1325519dd318b499acc3e91d47c2e4 1 parent 60dc558
@erylee authored
View
5 src/apps/emqtt/include/emqtt.hrl
@@ -60,6 +60,11 @@
arg
}).
+-record(topic, {
+ name,
+ sub
+}).
+
-record(sub, {
topic,
qos = 0
View
7 src/apps/emqtt/src/emqtt.erl
@@ -63,10 +63,13 @@
[{description, "core initialized"},
{requires, kernel_ready}]}).
--emqtt_boot_step({networking,
- [{mfa, {emqtt_networking, boot, []}},
+-emqtt_boot_step({router,
+ [{mfa, {emqtt_router, boot, []}},
{requires, core_initialized}]}).
+-emqtt_boot_step({networking,
+ [{mfa, {emqtt_networking, boot, []}},
+ {requires, router}]}).
%%---------------------------------------------------------------------------
View
29 src/apps/emqtt/src/emqtt_client.erl
@@ -27,6 +27,8 @@ start_link(Sock) ->
gen_server2:start_link(?MODULE, [Sock], []).
init([Sock]) ->
+ {A1,A2,A3} = now(),
+ random:seed(A1, A2, A3),
{ok, #state{sock = Sock}}.
%%--------------------------------------------------------------------
@@ -47,10 +49,27 @@ handle_call(_Req, _From, State) ->
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(#mqtt_packet{type = ?CONNECT}, #state{sock = Sock} = State) ->
- Reply = #mqtt_packet{type = ?CONNACK, arg = 0},
- send(Reply, Sock),
+ send(#mqtt_packet{type = ?CONNACK, arg = 0}, Sock),
{noreply, State};
+handle_cast(#mqtt_packet{type = ?PUBLISH, arg={Topic, Msg}}, State) ->
+ emqtt_router:publish(Topic, Msg),
+ {noreply, State};
+
+handle_cast(#mqtt_packet{type = ?SUBSCRIBE, arg=Subs}, #state{sock = Sock} = State) ->
+ [emqtt_router:subscribe(Topic, self()) ||
+ #sub{topic = Topic} <- Subs],
+ MsgId = random:uniform(16#FFFF),
+ send(#mqtt_packet{type = ?SUBACK, arg={MsgId, Subs}}, Sock),
+ {noreply, State};
+
+handle_cast(#mqtt_packet{type = ?PINGREQ}, #state{sock = Sock} = State) ->
+ send(#mqtt_packet{type = ?PINGRESP}, Sock),
+ {noreply, State};
+
+handle_cast(#mqtt_packet{type = ?DISCONNECT}, State) ->
+ {stop, normal, State};
+
handle_cast(Msg, State) ->
io:format("badmsg: ~p~n", [Msg]),
{noreply, State}.
@@ -61,6 +80,10 @@ handle_cast(Msg, State) ->
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
+handle_info({publish, Topic, Msg}, #state{sock = Sock} = State) ->
+ send(#mqtt_packet{type = ?PUBLISH, arg={Topic, Msg}}, Sock),
+ {noreply, State};
+
handle_info(_Info, State) ->
{noreply, State}.
@@ -72,6 +95,7 @@ handle_info(_Info, State) ->
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
+ emqtt_router:unsubscribe(self()),
ok.
%%--------------------------------------------------------------------
@@ -82,6 +106,7 @@ code_change(_OldVsn, State, _Extra) ->
{ok, State}.
send(#mqtt_packet{} = Message, Socket) ->
+ io:format("Message Sent: ~p~n", [Message]),
%%?LOG({mqtt_core, send, pretty(Message)}),
{VariableHeader, Payload} = emqtt_packet:encode_message(Message),
ok = send(emqtt_packet:encode_fixed_header(Message), Socket),
View
7 src/apps/emqtt/src/emqtt_mnesia.erl
@@ -142,7 +142,12 @@ table_definitions() ->
[{record_name, listener},
{attributes, record_info(fields, listener)},
{type, bag},
- {match, #listener{_='_'}}]}
+ {match, #listener{_='_'}}]},
+ {topic,%emqtt_topic
+ [{record_name, topic},
+ {attributes, record_info(fields, topic)},
+ {type, bag},
+ {match, #topic{_='_'}}]}
] ++ gm:table_definitions().
table_names() ->
View
36 src/apps/emqtt/src/emqtt_reader.erl
@@ -96,13 +96,15 @@ recvloop(Socket, ClientPid) ->
FixedHeader = recv(1, Socket),
io:format("fixed header: ~p~n", [FixedHeader]),
RemainingLength = recv_length(Socket),
- io:format("Length: ~p~n", [RemainingLength]),
Rest = recv(RemainingLength, Socket),
Header = emqtt_packet:decode_fixed_header(FixedHeader),
Message = emqtt_packet:decode_message(Header, Rest),
- io:format("Message: ~p~n", [Message]),
+ io:format("Message Recved: ~p~n", [Message]),
gen_server:cast(ClientPid, Message),
- recvloop(Socket, ClientPid).
+ case Message of
+ #mqtt_packet{type = ?DISCONNECT} -> stop;
+ _ -> recvloop(Socket, ClientPid)
+ end.
recv_length(Socket) ->
recv_length(recv(1, Socket), 1, 0, Socket).
@@ -111,13 +113,6 @@ recv_length(<<0:1, Length:7>>, Multiplier, Value, _Socket) ->
recv_length(<<1:1, Length:7>>, Multiplier, Value, Socket) ->
recv_length(recv(1, Socket), Multiplier * 128, Value + Multiplier * Length, Socket).
-send_length(Length, Socket) when Length div 128 > 0 ->
- Digit = Length rem 128,
- send(<<1:1, Digit:7/big>>, Socket),
- send_length(Length div 128, Socket);
-send_length(Length, Socket) ->
- Digit = Length rem 128,
- send(<<0:1, Digit:7/big>>, Socket).
recv(0, _Socket) ->
<<>>;
@@ -130,27 +125,6 @@ recv(Length, Socket) ->
exit(Reason)
end.
-send(#mqtt_packet{} = Message, Socket) ->
-%%?LOG({mqtt_packet_core, send, pretty(Message)}),
- {VariableHeader, Payload} = emqtt_packet:encode_message(Message),
- ok = send(emqtt_packet:encode_fixed_header(Message), Socket),
- ok = send_length(size(VariableHeader) + size(Payload), Socket),
- ok = send(VariableHeader, Socket),
- ok = send(Payload, Socket),
- ok;
-send(<<>>, _Socket) ->
-%%?LOG({send, no_bytes}),
- ok;
-send(Bytes, Socket) when is_binary(Bytes) ->
-%%?LOG({send,bytes,binary_to_list(Bytes)}),
- case gen_tcp:send(Socket, Bytes) of
- ok ->
- ok;
- {error, Reason} ->
- ?LOG({send, socket, error, Reason}),
- exit(Reason)
- end.
-
pretty(Message) when is_record(Message, mqtt_packet) ->
lists:flatten(
io_lib:format(
View
62 src/apps/emqtt/src/emqtt_router.erl
@@ -2,16 +2,25 @@
%%% File : emqtt_router.erl
%%% Author : Ery Lee <ery.lee@gmail.com>
%%% Purpose :
-%%% Created : 28 Dec. 2011
-%%% License : http://www.opengoss.com
+%%% Created : 03 Apr. 2010
+%%% License : http://www.monit.cn/license
%%%
-%%% Copyright (C) 2012, www.opengoss.com
+%%% Copyright (C) 2007-2010, www.monit.cn
%%%----------------------------------------------------------------------
-module(emqtt_router).
-author('ery.lee@gmail.com').
--behavior(gen_server).
+-include("emqtt.hrl").
+
+-export([boot/0,
+ dump/0,
+ subscribe/2,
+ unsubscribe/1,
+ unsubscribe/2,
+ publish/2]).
+
+-behavior(gen_server2).
-export([start_link/0]).
@@ -24,12 +33,40 @@
-record(state, {}).
+boot() ->
+ {ok, _} = supervisor:start_child(
+ emqtt_sup,
+ {emqtt_router,
+ {emqtt_router, start_link, []},
+ permanent, infinity, worker, [emqtt_router]}
+ ),
+ ok.
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+ gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+
+dump() ->
+ Keys = mnesia:dirty_all_keys(topic),
+ Topics = lists:flatten([mnesia:dirty_read(topic, Key) || Key <- Keys]),
+ [io:format("topic: ~p, sub: ~p~n", [Name, Pid])
+ || #topic{name = Name, sub = Pid} <- Topics].
+
+subscribe(Topic, Pid) ->
+ gen_server2:call(?MODULE, {subscribe, Topic, Pid}).
+
+unsubscribe(Topic, Pid) ->
+ gen_server2:cast(?MODULE, {unsubscribe, Topic, Pid}).
+
+unsubscribe(Pid) ->
+ gen_server2:cast(?MODULE, {unsubscribe, Pid}).
+
+publish(Topic, Msg) ->
+ Subscribers = mnesia:dirty_read(topic, Topic),
+ [Sub ! {publish, Topic, Msg} || #topic{sub = Sub} <- Subscribers].
%%====================================================================
%% gen_server callbacks
@@ -52,14 +89,28 @@ init([]) ->
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
+handle_call({subscribe, Topic, Pid}, _From, State) ->
+ Reply = mnesia:dirty_write(#topic{name = Topic, sub = Pid}),
+ {reply, Reply, State};
+
handle_call(_Req, _From, State) ->
{reply, {error, badreq}, State}.
+
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
+handle_cast({unsubscribe, Topic, Pid}, State) ->
+ mnesia:dirty_delete_object(#topic{name = Topic, sub = Pid}),
+ {noreply, State};
+
+handle_cast({unsubscribe, Pid}, State) ->
+ Subscribers = mnesia:dirty_match_object(#topic{sub=Pid}),
+ [mnesia:dirty_delete(Sub) || Sub <- Subscribers],
+ {noreply, State};
+
handle_cast(_Msg, State) ->
{noreply, State}.
@@ -92,4 +143,3 @@ code_change(_OldVsn, State, _Extra) ->
%%% Internal functions
%%--------------------------------------------------------------------
-
Please sign in to comment.
Something went wrong with that request. Please try again.