您的位置:首页 > 理论基础 > 计算机网络

erlang tcp服务器和客户端的简单实现

2014-09-21 16:39 344 查看
看了《erlang程序设计》的lib_chan代码,自己也试着写了一个,不过大部分代码都摘自lib_chan,做了一个改编。

-module(lib_comm).

-export([start_server/1, stop_server/1, start_client/2, send/2]).

stop_server(Port) when is_integer(Port) ->
ServerName = list_to_atom("portServer" ++ integer_to_list(Port)),
case whereis(ServerName) of
undefined ->
not_started;
Pid ->
exit(Pid, kill),
(catch unregister(ServerName)),
stopped
end.

start_server(Port) ->
io:format("start_server(), Pid: ~p~n", [self()]),
spawn(fun() -> start_port_server(Port) end).

start_port_server(Port) ->
io:format("start_port_server(), Pid: ~p~n", [self()]),
start_raw_server(Port, fun(Socket) -> start_port_instance(Socket) end, 100, 4).

start_raw_server(Port, Fun, Max, PacketLength) ->
io:format("start_raw_server(), Pid: ~p~n", [self()]),
ServerName = list_to_atom("portServer" ++ integer_to_list(Port)),
case whereis(ServerName) of
undefined ->
Self = self(),
Pid = spawn_link(fun() -> cold_start(Self,Port,Fun,Max,PacketLength) end),
receive
{Pid, ok} ->
register(ServerName, Pid),
{ok, self()};
{Pid, Error} ->
Error
end;
_Pid ->
{error, already_started}
end.

cold_start(Master,Port,Fun,Max,PacketLength) ->
io:format("cold_start(), Pid: ~p~n", [self()]),

process_flag(trap_exit, true),
io:format("Starting a port server on ~p...~n",[Port]),
case gen_tcp:listen(Port, [binary, {nodelay,true}, {packet, PacketLength}, {reuseaddr, true}, {active, true}]) of
{ok, Listen} ->
io:format("Listening to:~p~n",[Listen]),
Master ! {self(), ok},
_New = start_accept(Listen, Fun),

io:format("New 1 : ~p~n", [_New]),
socket_loop(Listen, Master, [], Fun, Max);
Error ->
Master ! {self(), Error}
end.

start_accept(Listen, Fun) ->
S = self(),
spawn_link(fun() -> start_child(S, Listen, Fun) end).

start_child(Parent, Listen, Fun) ->
io:format("Pid: ~p gen_tcp:accept ~n", [self()]),
case gen_tcp:accept(Listen) of
{ok, Socket} ->
io:format("Pid: ~p accepted ~n", [self()]),
Parent ! {istarted, self()}, % tell the controller
inet:setopts(Socket, [ {packet,4}, binary, {nodelay,true}, {active, true}]),
io:format("running the child:~p Fun=~p~n", [Socket, Fun]),
process_flag(trap_exit, true),
case (catch Fun(Socket)) of
{'EXIT', normal} ->
true;
{'EXIT', Why} ->
io:format("Port process dies with exit:~p~n",[Why]),
true;
_ ->
true %% not an exit so everything's ok
end
end.

socket_loop(Listen, Master, Active, Fun, Max) ->
receive
{istarted, Pid} ->
io:format("Pid = ~p istarted ~n", [Pid]),
Active1 = [Pid|Active],
possibly_start_another(Master, Listen,Active1,Fun,Max);
{'EXIT', Master, _Why} ->
io:format("Pid = ~p master exit, reason: ~p ~n", [Master, _Why]),
socket_loop(Listen, Master, Active, Fun, Max);
{'EXIT', Pid, _Why} ->
io:format("Pid = ~p exit, reason: ~p ~n", [Pid, _Why]),
Active1 = lists:delete(Pid, Active),
possibly_start_another(Master, Listen,Active1,Fun,Max);
_Other ->
io:format("socket_loop() _Other = ~p~n", [_Other]),
socket_loop(Listen, Master, Active, Fun, Max)
end.

possibly_start_another(Master, Listen, Active, Fun, Max) ->
case length(Active) of
N when N < Max ->
_New = start_accept(Listen, Fun),
io:format("New 2: ~p~n", [_New]),
socket_loop(Master, Listen, Active, Fun,Max);
_ ->
socket_loop(Master, Listen, Active, Fun, Max)
end.

start_port_instance(Socket) ->
process_flag(trap_exit, true),
MessageHandler = spawn_link(fun() -> start_server_biz() end),
message_loop(Socket, MessageHandler).

start_server_biz() ->
server_biz_loop().

server_biz_loop() ->
receive
{recv, Pid, Term} ->
io:format("server recv message ~p~n", [Term]),
Pid ! {send, Term},
server_biz_loop();
{closed, Pid} ->
Pid ! close
end.

message_loop(Socket, Pid) ->
receive
{tcp, Socket, Bin} ->
Term = binary_to_term(Bin),
Pid ! {recv, self(), Term},
message_loop(Socket, Pid);

{tcp_closed, Socket} ->
Pid ! {closed, self()};

{'EXIT', Pid, _Why} ->
gen_tcp:close(Socket);

close ->
gen_tcp:close(Socket);

{send, Term} ->
gen_tcp:send(Socket, term_to_binary(Term)),
message_loop(Socket, Pid);

UUg ->
io:format("protocol error:~p~n",[UUg]),
message_loop(Socket, Pid)
end.

start_client(Host, Port) ->
Self = self(),
spawn(fun() -> start_raw_client(Self, Host, Port) end),
receive
{ok, Pid} ->
Pid;
Error ->
io:format("client connect failure, ~p~n", [Error]),
Error
end.

start_raw_client(Master, Host, Port) ->
PacketLength = 4,
Self = self(),
Pid = spawn(fun() -> connect(Self, Host, Port, PacketLength) end),
client_biz_loop(Master, Pid).

connect(Parent, Host, Port, PacketLength) ->
case gen_tcp:connect(Host, Port, [binary, {active, true}, {packet, PacketLength}]) of
{ok, Socket} ->
Parent ! {ok, self()},
message_loop(Socket, Parent);
Error ->
Parent ! {self(), Error}
end.

client_biz_loop(Master, Pid) ->
receive
{ok, Pid} ->
Pid ! {send, "hello world"},
Master ! {ok, Pid},
client_biz_loop(Master, Pid);
{recv, Pid, Term} ->
io:format("client recv message: ~p~n", [Term]),
client_biz_loop(Master, Pid);
Error ->
Master ! Error,
Error
end.

send(Pid, Term) ->
Pid ! {send, Term}.

按照下面方式调用:
lib_comm:start_server(8080).

Pid = lib_comm:start_client("localhost", 8080).

lib_comm:send(Pid, "aaaaa").
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  erlang tcp