-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.