-module(socket).
-behavior(gen_server).
-export([
start/0,
do_client_connect/0
]).
-export([
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
-define(TCP_OPTIONS,[binary, {packet, 4}, {nodelay, true}, {reuseaddr, true}, {active, true}]).
-define(LISTEN_PORT,12345).
do_client_connect() ->
gen_tcp:connect("localhost",12345,[]).
start() ->
{ok,LSocket} = gen_tcp:listen(?LISTEN_PORT,?TCP_OPTIONS),
io:format("start. LSocket:~w~n",[LSocket]),
%% 这里启动了两个gen_server进程去accept。当有连接请求过来时,哪个进程空闲就由哪个去处理;
{ok,Pid1} = gen_server:start_link({local,a},?MODULE,[LSocket],[]),
io:format("start. Pid1:~w~n",[Pid1]),
{ok,Pid2} = gen_server:start_link({local,b},?MODULE,[LSocket],[]),
io:format("start. Pid2:~w~n",[Pid2]),
%%gen_tcp:controlling_process(Pid,LSocket).
ok.
init([LSocket]) ->
do_accept(LSocket),
{ok,LSocket}.
handle_info({inet_async, LSock, _Ref, {ok, Sock}},LSock) ->
io:format("handle_info. pid:~w~n",[self()]),
io:format("handle_info. accepted one LSock:~w~n",[LSock]),
io:format("handle_info. accepted one Sock:~w~n",[Sock]),
%%do_accept(LSock). %%这里本不应该注释掉的。注释掉的目的是为了测试:先有客户端请求connect,然后再手动发消息accept给服务端让服务端执行prim_inet:async_accept监听。实验证明是可以的;
{noreply,LSock};
handle_info(accept,LSock) ->
do_accept(LSock),
{noreply,LSock}.
handle_cast(_Msg,_State) ->
{noreply,_State}.
handle_call(_Request,_From,_State) ->
{noreply,_State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
do_accept(LSocket) ->
%%经过测试,发现,可以是服务端先accept,然后客户端connect;也可以是客户端先connect,然后服务端再accept;都可以建立socket连接;
case prim_inet:async_accept(LSocket, -1) of
{ok, _Ref} ->
ok;
_Error ->
ok
end.