其實也不能算是我的了,是我去年從mochiweb里扣出來的最最最基本的TCP通信代碼, mochiweb是一個開源的高併發HTTP服務器,據說可以打造百萬級應用。 mochiweb 有上萬行,我摳出來只有百多行,算是夠精簡的了,現在貼出來,方便像我一樣的菜鳥學習,造福人類。其實也是方便我自己,放硬盤上估計過段時間就找不到了。很久沒有看這語言了,改不動,所以特別感謝北京-CS幫我修改了錯誤。
功能很簡單,就是收到什麼數據,就發送什麼數據,可以作爲很好的入門代碼,就不解釋那麼多了,直接貼代碼:
my_server模塊如下:
- -module(my_server).
- -export([start/0, stop/0, looper/1]).
- %這個Options我本來打算從配置文件裏讀取,然後組裝成一個record,但是不大熟,所以這裏沒有用到它
- %以後用得上再改
- -define(Options, [
- {port, 8442},
- {loop, {?MODULE, loop}},
- {name, ?MODULE}
- ]).
- -record(qiao_socket_server,
- {port=8024,
- loop,
- name,
- ip=any,
- nodelay=false,
- backlog=128,
- listen=null,
- active_sockets=0}
- ).
- start() ->
- %%socket_server:start(?Options).
- socket_server:start(#qiao_socket_server{loop = {?MODULE, looper}}).
- stop() ->
- socket_server:stop(?Options).
- %關閉服務的我也沒有改,所以這裏有個錯誤。
- looper(Socket) ->
- inet:setopts(Socket, [{active, once}]),
- receive
- {tcp, Socket, Data} ->
- io:format("Got packet: ~p~n", [Data]), %接收到什麼數據,就發生什麼數據
- gen_tcp:send(Socket, Data),
- looper(Socket);
- {tcp_closed, Socket}->
- io:format("Socket ~p closed~n", [Socket]);
- {tcp_error, Socket, Reason} ->
- io:format("Error on socket ~p reason: ~p~n", [Socket, Reason])
- end.
socket_server模塊代碼如下:
- -module(socket_server).
- -author('huanghongqiao').
- -behaviour(gen_server).
- -export([start/0,start/1, stop/1]).
- -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3,
- handle_info/2]).
- -define(RECBUF_SIZE, 8192).
- -record(qiao_socket_server,
- {port=8024,
- loop,
- name,
- ip=any,
- nodelay=false,
- backlog=128,
- listen=null,
- active_sockets=0}
- ).
- %%客戶端函數, start 開啓服務,stop 關閉服務
- start() ->
- gen_server:start(?MODULE, [], []).
- start(InitArg = #qiao_socket_server{name = Name}) ->
- case Name of
- undefined ->
- gen_server:start(?MODULE, InitArg, []);
- _ ->
- gen_server:start(Name, ?MODULE, InitArg, [])
- end.
- stop(Name) when is_atom(Name) ->
- gen_server:cast(Name, stop);
- stop(Pid) when is_pid(Pid) ->
- gen_server:cast(Pid, stop);
- stop(State = #qiao_socket_server{name = Name}) ->
- stop(Name).
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% 回調函數
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- init(State = #qiao_socket_server{ip=Ip, port=Port, backlog=Backlog, nodelay=NoDelay}) ->
- BaseOpts = [binary,
- {reuseaddr, true},
- {packet, 0},
- {backlog, Backlog},
- {recbuf, ?RECBUF_SIZE},
- {active, false},
- {nodelay, NoDelay}],
- listen(Port, BaseOpts, State). %%監聽
- handle_call(_Msg, _Caller, State) -> {noreply, State}.
- handle_info(_Msg, Library) -> {noreply, Library}.
- terminate(Reason, #qiao_socket_server{listen=Listen}) ->
- io:format("socket close,~p~n",[Reason]),
- gen_tcp:close(Listen). %%關閉Listen
- code_change(_OldVsn, State, _Extra) ->
- State.
- handle_cast({accepted, Pid}, State=#qiao_socket_server{active_sockets=ActiveSockets,
- listen = Listen, loop = Loop}) ->
- NewState = State#qiao_socket_server{active_sockets=1 + ActiveSockets},
- socket_server_acceptor:start_link(self(), Listen, Loop),
- {noreply, NewState}.
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- listen(Port, Opts, State) ->
- case gen_tcp:listen(Port, Opts) of
- {ok, Listen} ->
- socket_server_acceptor:start_link(self(), Listen, State#qiao_socket_server.loop),
- NewState = State#qiao_socket_server{listen = Listen},
- {ok, NewState};
- {error, Reason} ->
- {stop, Reason}
- end.
socket_server_acceptor模塊代碼如下:
- -module(socket_server_acceptor).
- -export([start_link/3, init/3]).
- start_link(Server, Listen, Loop) ->
- proc_lib:spawn_link(?MODULE, init, [Server, Listen, Loop]).
- init(Server, Listen, Loop) ->
- case catch gen_tcp:accept(Listen) of
- {ok, Socket} ->
- gen_server:cast(Server, {accepted, self()}),
- io:format("socket init,~p~n", [self()]),
- call_loop(Loop, Socket);
- {error, closed} ->
- io:format("socket close,~n"),
- exit(normal);
- {error, timeout} ->
- io:format("socket timeout"),
- init(Server, Listen, Loop);
- {error, esslaccept} ->
- io:format("socket close2,~n"),
- exit(normal);
- R ->
- io:format("socket close1,~p~n",[R]),
- exit({error, accept_failed})
- end.
- call_loop({M, F}, Socket) ->
- io:format("socket call_loop,~n"),
- M:F(Socket);
- call_loop({M, F, [A1]}, Socket) ->
- M:F(Socket, A1);
- call_loop({M, F, A}, Socket) ->
- erlang:apply(M, F, [Socket | A]);
- call_loop(Loop, Socket) ->
- Loop(Socket).
運行效果見下圖,具體能支持多少併發,我也沒有測試過,哈哈: