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").
-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").
相关文章推荐
- 使用套接字实现简单TCP服务器客户端模型
- Golang实现简单tcp服务器02 -- 实现echo服务器/客户端
- 从简单到复杂实现TCP客户端服务器通信
- Golang实现简单tcp服务器03 -- 文本广播式聊天服务器/客户端
- TCP服务器、客户端的简单实现
- Linux下的C语言编程——简单实现tcp客户端和服务器
- python socket实现简单的(TCP/UDP)服务器/客户端
- golang简单实现一个基于TLS/SSL的 TCP服务器和客户端
- TCP SOCKET服务器|客户端简单实现
- Java 利用套接字Socket实现简单的服务器与客户端通信
- 网络编程资料总结(二)----Tcp多线程服务器和客户端的实现
- Linux 网络编程基础(一) ---------------客户端/服务器的简单实现
- 使用Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- 简单的TCP协议 socket编程(C语言版服务器和客户端)
- Linux 网络编程基础(一) ---------------客户端/服务器的简单实现
- java 通过 socket 实现 服务器和客户端的通信 TCP
- Linux 网络编程基础 客户端/服务器的简单实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- C++ 简单的 Tcp 实现[socket] 客户端与客户端通信
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现