2015-11-24 43 views
1

當我運行我的服務器和客戶端時,服務器收到一個請求,但是當調用protocol/3方法時,它只是停止。我用調試器進行了檢查,它只是在函數調用上,但沒有任何反應。目前的典型運行如下所示:服務器沒有收到請求後繼續

server:start(). 

S = client:start(). 
client:authenticate(S, "Anon"). 

此時它只是停止工作。請注意,我在兩個獨立的erlang外殼上運行服務器和客戶端。

Server代碼

start() -> 
    io:format("Starting Server..."), 
    Clients = dict:new(), 
    Database = spawn_link(fun() -> database(Clients)end), 
    case gen_tcp:listen(1992, [binary, {active, false}]) of 
     {ok, ListeningSocket} -> 
      io:format("ok~n"), 
      accept(ListeningSocket, Database), 
      {ok, Port} = inet:port(ListeningSocket); 
     {error, Reason} -> 
      io:format("Failed: ~s", [Reason]), 
      {error, Reason} 
    end. 

database(Clients) -> 
receive 
    {get_client, From, User} -> 
     case dict:find(User, Clients) of 
      {ok, UserSocket} -> 
       From ! {id, UserSocket}; 
      error -> 
       From ! error 
     end, 
     database(Clients); 
    {ins_client, From, Socket, User} -> 
     case dict:find(User, Clients) of 
      {ok, _} -> 
       From ! error; 
      error -> 
       Updated = dict:store(User, Socket, Clients), 
       From ! ok, 
       database(Updated) 
     end 
end. 

accept(ListeningSocket, Database) -> 
    io:format("Accepting..."), 
    case gen_tcp:accept(ListeningSocket) of 
     {ok, Socket} -> 
      io:format("ok~n"), 
      Pid = spawn(fun() -> loop(Socket, Database)end), 
      gen_tcp:controlling_process(Socket, Pid); 
     {error, Reason} -> 
      io:format("Failed: ~s", [Reason]) 
    end, 
    accept(ListeningSocket, Database). 

loop(Socket, Database) -> 
    inet:setopts(Socket, [{active, once}]), 
    io:format("Waiting for request~n"), 
    receive 
     {tcp, Socket, Data} -> 
      Request = binary_to_term(Data), 
      protocol(Socket, Request, Database), 
      loop(Socket, Database); 
     {tcp_closed, Socket} -> 
      io:format("Socket ~s closed. ~n", [Socket]), 
      gen_tcp:close(Socket) 
    end. 

stop(Socket) -> 
    gen_tcp:close(Socket). 

protocol(Socket, Request, Database) -> 
case Request of 
    {message, To, Message} -> 
     io:format("Message received: for ~s ~s", [To, Message]), 
     Database ! {get_client, self(), To}, 
     receive 
      {id, Receiver} -> 
       gen_tcp:send(Receiver, term_to_binary(Message)), 
       io:format("Sent to ~s: ~s~n", [Receiver, Message]); 
      error -> 
       gen_tcp:send(Socket, term_to_binary("User not found")) 
     end; 
    {enter, User} -> 
     io:format("Request: {Enter: ~s}", User), 
     Database ! {ins_client, self(), User}, 
     receive 
      ok -> 
       gen_tcp:send(Socket, term_to_binary("Authenticated")); 
      error -> 
       gen_tcp:send(Socket, term_to_binary("Authentication failed")) 
     end; 
    Other -> 
     io:format("This happened: ~s", [Other]) 
end. 

客戶端代碼

start() -> 
    io:format("Client starting..."), 
    case gen_tcp:connect({127,0,0,1}, 1992, [binary, {active, false}]) of 
     {ok, Socket} -> 
      io:format("ok.\n"), 
      spawn(fun() -> loop(Socket)end), 
      Socket; 
     {error, Reason} -> 
      io:format("Failed: ~s", [Reason]), 
      {error, Reason} 
    end. 

loop(Socket) -> 
    inet:setopts(Socket, [{active, once}]), 
    io:format("Waiting for data...~n"), 
    receive 
     {tcp_closed, Socket} -> 
      io:format("Server ended connection.~n"), 
      disconnect(Socket); 
     {tcp, Socket, Data} -> 
      io:format("From Server:' ~s' ~n", [Data]) 
    end. 

authenticate(Socket, Username) -> 
    inet:setopts(Socket, [{active, once}]), 
    Term = {enter, Username}, 
    Request = term_to_binary(Term), 
    gen_tcp:send(Socket, Request). 

send(Socket, To, Input) -> 
    inet:setopts(Socket, [{active, once}]), 
    Message = term_to_binary({message, To, Input}), 
    gen_tcp:send(Socket, Message). 

disconnect(Socket) -> 
    gen_tcp:close(Socket). 

對不起任何格式錯誤,我仍然很新的這:)

回答

2

的原因您掛起是您的​​消息到您的數據庫進程缺少Socket元素。數據庫進程只是忽略不正確的​​消息,將其留在消息隊列中。

一些其他的事情來解決:

  • 接受連接後,你的服務器產生一個接收循環過程且隨後將新的套接字到新過程的控制。最好產生一個新的接受者並直接調用循環,因爲這樣就不需要改變新的套接字的控制過程。

    accept(ListeningSocket, Database) -> 
        io:format("Accepting..."), 
        case gen_tcp:accept(ListeningSocket) of 
         {ok, Socket} -> 
          io:format("ok~n"), 
          spawn(fun() -> accept(ListeningSocket, Database) end), 
          loop(Socket, Database); 
         {error, Reason} -> 
          io:format("Failed: ~s", [Reason]), 
          error(Reason) 
        end. 
    
  • 在您的服務器功能,您必須在io:format錯誤在那裏你要打印的用戶:在User參數需要在列表中。

  • 您的函數是一個具有內部case語句的大函數。倒不如寫爲多個條款,每一個期望的消息:

    protocol(Socket, {message, To, Message}, Database) -> 
        io:format("Message received: for ~s ~s", [To, Message]), 
        ...; 
    protocol(Socket, {enter, User}, Database) -> 
        io:format("Request: {Enter: ~s}", [User]), 
        ...; 
    protocol(_Socket, Other, _Database) -> 
        io:format("This happened: ~s", [Other]). 
    
  • 您的客戶端的loop功能是奇數,因爲它不是一個循環。這更像是一個簡單的接收功能,所以你可能想重新命名它。

  • start函數調用loop函數是毫無意義的,因爲新產生的進程不是套接字的控制進程,所以它從不接收任何東西。

  • 您應該使loop子句處理{tcp, Socket, Data}返回Data,因此它的調用者可以處理返回的數據。

  • 這將是更好,如果你的客戶的start功能成功返回{ok, Socket}而不僅僅是Socket,因爲這使得它更容易爲呼叫者成功和失敗之間的區別。

+0

非常感謝你,我知道我的問題,我會成爲這樣的事情......不幸的是它發生在每個人身上非常感謝您的回答,因爲您不僅回答了我的問題,還幫助了​​我的其餘申請。不勝感激! – Bangosushi

+0

但是,我有一個循環函數的問題。正如你可能從我的代碼中看到的那樣,我不是功能編程領域的強大程序員,特別是Erlang。我如何讓我的客戶端不僅能夠調用函數(例如來自用戶),還能夠打印出消息。因爲我需要返回Socket才能使用特定的客戶端,但是我無法在產生的進程中使用print語句。 – Bangosushi

+0

要回答你的問題,需要知道你是否期望你的客戶端能夠接收不是作爲回覆的消息,而是被動地接收服務器?如果是這樣,你應該考慮使用'gen_server',因爲它可以接受調用(例如調用來發送套接字上的東西),同時還監聽和處理傳入的消息。這需要'gen_server'進程成爲套接字的控制進程。順便說一句,你可以在產生的進程中調用'io:format',但是當然如果它們試圖同時發送文本,它的輸出可能會與其他進程的輸出混雜在一起。 –

相關問題