2013-05-05 2 views
2

В настоящее время я пишу простой сервер, который будет работать с клиентами, подключающимися, а затем разговаривая с eachother, где сервер выступает в качестве посредника.Erlang tcp server/client messages messages

Установка является:

  • Сервер начинает
  • 2 Клиенты подключается к серверу
  • Client1/2 посылает сообщение с уникальным идентификатором (атом)
  • Сервер сохраняет этот идентификатор вместе с Socket PID
  • Client1 отправляет {send_to_id, client2id, Message}
  • Серверные сообщения Клиент2

Но это не работает для меня, я получаю ошибку function_clause.

Так что в основном я хочу отправить сообщение клиенту с tcp_send, не имея клиентский сервер, но только с recv. Это возможно?

Серверный код:

-export([start/1]). 

-define(TCP_OPTIONS, [binary, {packet, 2}, {active, false}, {reuseaddr, true}]). 
-define(PORT, 8080). 

-spec start(Port) -> pid() when 
     Port::integer(). 
start(Port) -> 
    process_flag(trap_exit, true), 
    ClientDict = dict:new(), 
    GamesDict = dict:new(), 
    HandlerPID = spawn_link(fun() -> handler(ClientDict, GamesDict) end), 
    imup_listener:listen(Port, HandlerPID). 





%%------------------------------ 
% Internal Functions 
%%------------------------------ 

handler(Clients, Games) -> 
    receive 
    {insert_client, Socket, Alias} -> 
     TmpClients = dict:append(Socket, Alias, Clients), 
     TmpClients2 = dict:append(Alias, Socket, TmpClients), 
     handler(TmpClients2, Games); 
    {get_client_id, ReceiverPID, ClientPID} -> 
     {ok , CID} = dict:find(ClientPID, Clients), 
     ReceiverPID ! {id, CID}, 
     handler(Clients, Games); 
    {get_client_pid, ReceiverPID, ClientID} -> 
     {ok, CPID} = dict:find(ClientID, Clients), 
     ReceiverPID ! {pid, CPID}, 
     handler(Clients, Games); 
    {host_game, HostID, GameID} -> 
     TmpGames = dict:append_list(GameID, [HostID], Games), 
     handler(Clients, TmpGames); 
    {add_player, PlayerID, GameID} -> 
     TmpGames = dict:append_list(GameID, [PlayerID], Games), 
     handler(Clients, TmpGames); 
    {get_host, ReceiverPID, GameID} -> 
     {ok, [HID|T]} = dict:find(GameID, Games), 
     {ok, HPID} = dict:find(HID, Clients), 
     ReceiverPID ! {host_is, HID, HPID}, 
     handler(Clients, Games); 

    _ -> 
     {error, "I don't know what you want from me :("} 
    end. 

Слушатель код: Код

-export([listen/2]). 

-define(TCP_OPTIONS, [binary, {packet, 2}, {active, false}, {reuseaddr, true}]). 

listen(Port, HandlerPID) -> 
    {ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS), 
    spawn_link(fun() -> accept(LSocket, HandlerPID) end), 
    LSocket. 




% Wait for incoming connections and spawn a process that will process incoming packets. 
accept(LSocket, HandlerPID) -> 
    {ok, Socket} = gen_tcp:accept(LSocket), 
    Pid = spawn(fun() -> 
      io:format("Connection accepted ~n", []), 
      %%DictPID ! {insert, Socket, Socket}, 
      loop(Socket, HandlerPID) 
     end), 
    gen_tcp:controlling_process(Socket, Pid), 
    accept(LSocket, HandlerPID). 



% Echo back whatever data we receive on Socket 
loop(Sock, HandlerPID) -> 
    inet:setopts(Sock, [{active, once}]), 
    receive 
    {tcp, Socket, Data} -> 
     io:format("Got packet: ~p == ", [Data]), 

     FormatedData = process_data(Socket, Data, HandlerPID), 
     io:format("~p~n", [FormatedData]), 
     convey_message(Socket, FormatedData), 

     loop(Socket, HandlerPID); 
    {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. 




%%------------------------------ 
% Internal Functions 
%%------------------------------ 


-spec process_data(S, D, P) -> term() when 
     S::port(), 
     D::binary(), 
     P::pid(). 
process_data(Socket, Data, HandlerPID) when is_binary(Data) -> 
    case binary_to_term(Data) of 
    {send_host, GameID, Msg} -> 
     HandlerPID ! {get_host, self(), GameID}, 
     receive 
     {host_is, _HID, HSOCK} -> 
      HSOCK; 
     _ -> 
      {error, nohost} 
     end, 
     Msg; 
    {send_all, GameID, Msg} -> 
     Msg; 
    {send_to_id, ReceiverID, Msg} -> 
     HandlerPID ! {get_client_pid, self(), ReceiverID}, 
     receive 
      {pid, SockPID} -> 
      gen_tcp:send(SockPID, term_to_binary(Msg)); 
     _ -> 
      {error, noid} 
     end, 
     term_to_binary({ok, delivered}); 
    {host_game, GameID} -> 
     GameID; 
    {join_game, GameID} -> 
     GameID; 
    {start_game, GameID} -> 
     GameID; 
    {enter, SenderID} -> 
     HandlerPID ! {insert_client, Socket, SenderID}; 
    Dat -> 
     Dat 
    end; 
process_data(Socket, Data, DictPID) -> 
    Data. 


convey_message(Socket, Data) when is_binary(Data) -> 
    gen_tcp:send(Socket, Data); 
convey_message(Socket, Data) -> 
    gen_tcp:send(Socket, term_to_binary(Data)). 

Клиент:

-export([connect/1, connect/2, disconnect/1, send/2, recv/1]). 

connect(PortNo) -> 
    {ok, Socket} = gen_tcp:connect("localhost", PortNo, [{active, false}, {packet, 2}]), 
    spawn(fun() -> recv(Socket) end), 
    Socket. 

connect(IP, PortNo) -> 
    {ok, Socket} = gen_tcp:connect(IP, PortNo, [{active, false}, {packet, 2}]), 
    spawn(fun() -> recv(Socket) end), 
    Socket. 


send(Socket, Message) -> 
    BinMsg = term_to_binary(Message), 
    gen_tcp:send(Socket, BinMsg). 
%% {ok, A} = gen_tcp:recv(Socket, 0), 
    %%A. 

recv(Socket) -> 
    {ok, A} = gen_tcp:recv(Socket, 0), 
    io:format("Received: ~p~n", [A]), 
    recv(Socket). 


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

Вы рекомендуете мне переписать все или моя идея как-то возможна? Спасибо заранее!

EDIT: Добавлен теструн.

Eshell V5.8.5 (abort with ^G) 
1> imup_server:start(1234). 
#Port<0.669> 
2> Socket1 = imup_client:connect(1234). 
Connection accepted 
#Port<0.681> 
3> Socket2 = imup_client:connect(1234). 
Connection accepted 
#Port<0.683> 
4> imup_client:send(Socket1, {enter, cOne}). 
ok 
Got packet: <<131,104,2,100,0,5,101,110,116,101,114,100,0,4,99,79,110,101>> == {insert_client,#Port<0.682>,cOne} 
Received: [131,104,3,100,0,13,105,110,115,101,114,116,95,99,108,105,101,110, 
      116,102,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115, 
      116,0,0,2,170,0,100,0,4,99,79,110,101] 
5> imup_client:send(Socket2, {enter, cTwo}). 
ok 
Got packet: <<131,104,2,100,0,5,101,110,116,101,114,100,0,4,99,84,119,111>> == {insert_client,#Port<0.684>,cTwo} 
Received: [131,104,3,100,0,13,105,110,115,101,114,116,95,99,108,105,101,110, 
      116,102,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115, 
      116,0,0,2,172,0,100,0,4,99,84,119,111] 
6> imup_client:send(Socket1, {send_to_id, cTwo, hello}). 
ok 
Got packet: <<131,104,3,100,0,10,115,101,110,100,95,116,111,95,105,100,100,0,4, 
       99,84,119,111,100,0,5,104,101,108,108,111>> == 7> 
=ERROR REPORT==== 5-May-2013::23:25:49 === 
Error in process <0.39.0> with exit value: {function_clause,[{gen_tcp,send,[[#Port<0.684>],<<9 bytes>>]},{imup_listener,process_data,3},{imup_listener,loop,2}]} 


=ERROR REPORT==== 5-May-2013::23:25:49 === 
Error in process <0.40.0> with exit value: {{badmatch,{error,closed}},[{imup_client,recv,1}]} 
+0

Чтобы отладить это, было бы неплохо увидеть сообщение об ошибке ... предложение функции, по моему опыту, часто просто опечатка или что-то в этом роде. –

+0

Конечно! Забыл добавить это, теперь он добавлен к дну. – Klesken

ответ

5

Таким образом, вы получаете сообщение об ошибке при вызове function_clausegen_tcp:send с аргументами [#Port<0.684>] и <<9 bytes>>. Первый аргумент - это список, содержащий «порт» (в данном случае это сокет), но это должен быть только порт.

Если я правильно читаю код, это происходит потому, что вы помещаете сокеты в словарь с dict:append, что приводит к тому, что значения словаря являются списками. Если вам действительно не нужно хранить несколько сокетов для каждого клиента или наоборот, возможно, более подходящим может быть dict:store.

+0

Спасибо, я, должно быть, был слеп, так как я даже тестировал функции dict, теперь я могу получать сообщения. Другая проблема возникла, хотя при приеме на сервере она двоичная, но когда клиент получает ее, она становится списком. Я понятия не имею, почему это происходит, поскольку я использую тот же term_to_binary/binary_to_term для обоих отправлений. Есть идеи? – Klesken

+2

Ваш клиент должен открыть Socket как двоичный файл, если вы хотите получать сообщения в виде двоичных данных.Добавьте двоичный код атома в список параметров при открытии tcp Socket в клиенте: gen_tcp: connect (IP, PortNo, [binary, {active, false}, {packet, 2}]) – Dave

+0

Ну, конечно: | Благодаря ! – Klesken