我的高併發TCP服務器

  其實也不能算是我的了,是我去年從mochiweb里扣出來的最最最基本的TCP通信代碼, mochiweb是一個開源的高併發HTTP服務器,據說可以打造百萬級應用。 mochiweb 有上萬行,我摳出來只有百多行,算是夠精簡的了,現在貼出來,方便像我一樣的菜鳥學習,造福人類。其實也是方便我自己,放硬盤上估計過段時間就找不到了。很久沒有看這語言了,改不動,所以特別感謝北京-CS幫我修改了錯誤。

  功能很簡單,就是收到什麼數據,就發送什麼數據,可以作爲很好的入門代碼,就不解釋那麼多了,直接貼代碼:

my_server模塊如下:

  1. -module(my_server). 
  2. -export([start/0, stop/0, looper/1]). 
  3.  
  4.  %這個Options我本來打算從配置文件裏讀取,然後組裝成一個record,但是不大熟,所以這裏沒有用到它
  5. %以後用得上再改
  6. -define(Options, [ 
  7.             {port, 8442}, 
  8.             {loop, {?MODULE, loop}}, 
  9.             {name, ?MODULE} 
  10.             ]). 
  11.              
  12.  -record(qiao_socket_server, 
  13.         {port=8024,                        
  14.          loop,                 
  15.          name,               
  16.          ip=any,                      
  17.          nodelay=false
  18.          backlog=128, 
  19.          listen=null,                 
  20.          active_sockets=0}             
  21.         ). 
  22.              
  23. start() -> 
  24.     %%socket_server:start(?Options). 
  25.     socket_server:start(#qiao_socket_server{loop = {?MODULE, looper}}).  
  26.  
  27. stop() -> 
  28.     socket_server:stop(?Options). 
  29. %關閉服務的我也沒有改,所以這裏有個錯誤。     
  30. looper(Socket) -> 
  31.     inet:setopts(Socket, [{active, once}]), 
  32.     receive 
  33.     {tcp, Socket, Data} -> 
  34.         io:format("Got packet: ~p~n", [Data]),  %接收到什麼數據,就發生什麼數據
  35.         gen_tcp:send(Socket, Data), 
  36.         looper(Socket); 
  37.     {tcp_closed, Socket}-> 
  38.         io:format("Socket ~p closed~n", [Socket]); 
  39.     {tcp_error, Socket, Reason} -> 
  40.         io:format("Error on socket ~p reason: ~p~n", [Socket, Reason]) 
  41.     end. 

 socket_server模塊代碼如下:

 

  1. -module(socket_server). 
  2. -author('huanghongqiao'). 
  3. -behaviour(gen_server). 
  4.   
  5. -export([start/0,start/1, stop/1]). 
  6. -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3, 
  7.          handle_info/2]). 
  8.   
  9.   
  10.  -define(RECBUF_SIZE, 8192). 
  11.  
  12.  -record(qiao_socket_server, 
  13.         {port=8024,                        
  14.          loop,                 
  15.          name,               
  16.          ip=any,                      
  17.          nodelay=false
  18.          backlog=128, 
  19.          listen=null,                 
  20.          active_sockets=0}             
  21.         ). 
  22. %%客戶端函數, start 開啓服務,stop 關閉服務 
  23. start() -> 
  24.     gen_server:start(?MODULE, [], []). 
  25. start(InitArg = #qiao_socket_server{name = Name}) -> 
  26.     case Name of 
  27.         undefined -> 
  28.         gen_server:start(?MODULE, InitArg, []); 
  29.         _ -> 
  30.             gen_server:start(Name, ?MODULE, InitArg, []) 
  31.     end. 
  32.     
  33. stop(Name) when is_atom(Name) -> 
  34.     gen_server:cast(Name, stop); 
  35. stop(Pid) when is_pid(Pid) -> 
  36.     gen_server:cast(Pid, stop); 
  37. stop(State = #qiao_socket_server{name = Name}) -> 
  38.     stop(Name). 
  39.   
  40.   
  41. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
  42.                  %% 回調函數  
  43. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
  44. init(State = #qiao_socket_server{ip=Ip, port=Port, backlog=Backlog, nodelay=NoDelay}) -> 
  45.     BaseOpts = [binary, 
  46.         {reuseaddr, true}, 
  47.         {packet, 0}, 
  48.         {backlog, Backlog}, 
  49.         {recbuf, ?RECBUF_SIZE}, 
  50.         {active, false}, 
  51.         {nodelay, NoDelay}], 
  52.     listen(Port, BaseOpts, State).    %%監聽         
  53.   
  54. handle_call(_Msg, _Caller, State) -> {noreply, State}. 
  55.      
  56. handle_info(_Msg, Library) -> {noreply, Library}. 
  57.  
  58. terminate(Reason, #qiao_socket_server{listen=Listen}) -> 
  59.     io:format("socket close,~p~n",[Reason]), 
  60.     gen_tcp:close(Listen).             %%關閉Listen 
  61.  
  62. code_change(_OldVsn, State, _Extra) -> 
  63.     State. 
  64.  
  65. handle_cast({accepted, Pid}, State=#qiao_socket_server{active_sockets=ActiveSockets,  
  66.             listen = Listen, loop = Loop}) -> 
  67.     NewState = State#qiao_socket_server{active_sockets=1 + ActiveSockets}, 
  68.     socket_server_acceptor:start_link(self(), Listen, Loop), 
  69.     {noreply, NewState}. 
  70.  
  71.  
  72. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
  73.  
  74. listen(Port, Opts, State) -> 
  75.     case gen_tcp:listen(Port, Opts) of 
  76.         {ok, Listen} -> 
  77.             socket_server_acceptor:start_link(self(), Listen, State#qiao_socket_server.loop), 
  78.             NewState = State#qiao_socket_server{listen = Listen}, 
  79.             {ok, NewState}; 
  80.         {error, Reason} -> 
  81.             {stop, Reason} 
  82.     end. 
  83.   

socket_server_acceptor模塊代碼如下:

 

  1. -module(socket_server_acceptor). 
  2. -export([start_link/3, init/3]). 
  3.  
  4. start_link(Server, Listen, Loop) -> 
  5.     proc_lib:spawn_link(?MODULE, init, [Server, Listen, Loop]). 
  6.  
  7. init(Server, Listen, Loop) -> 
  8.     case catch gen_tcp:accept(Listen) of 
  9.         {ok, Socket} -> 
  10.             gen_server:cast(Server, {accepted, self()}), 
  11.         io:format("socket init,~p~n", [self()]), 
  12.             call_loop(Loop, Socket); 
  13.         {error, closed} -> 
  14.         io:format("socket close,~n"), 
  15.             exit(normal); 
  16.         {error, timeout} -> 
  17.         io:format("socket timeout"), 
  18.             init(Server, Listen, Loop); 
  19.         {error, esslaccept} -> 
  20.         io:format("socket close2,~n"), 
  21.             exit(normal); 
  22.         R -> 
  23.         io:format("socket close1,~p~n",[R]), 
  24.             exit({error, accept_failed}) 
  25.     end. 
  26.  
  27. call_loop({M, F}, Socket) -> 
  28.     io:format("socket call_loop,~n"), 
  29.     M:F(Socket); 
  30. call_loop({M, F, [A1]}, Socket) -> 
  31.     M:F(Socket, A1); 
  32. call_loop({M, F, A}, Socket) -> 
  33.     erlang:apply(M, F, [Socket | A]); 
  34. call_loop(Loop, Socket) -> 
  35.     Loop(Socket). 

運行效果見下圖,具體能支持多少併發,我也沒有測試過,哈哈:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章