Erlang tcp服务器/客户端发送消息

Erlang tcp服务器/客户端发送消息,tcp,erlang,client-server,gen-tcp,Tcp,Erlang,Client Server,Gen Tcp,我目前正在编写一个简单的服务器,它将与客户端连接,然后在服务器充当中介的情况下相互交谈 设置为: 服务器启动 2个客户端连接到服务器 Client1/2发送具有唯一ID的消息(atom) 服务器将此ID与套接字PID一起保存 Client1发送{send_to_id,client2id,Message} 服务器消息客户端2 但这对我不起作用,我得到了一个函数\子句错误 因此,基本上我想用tcp_send向客户机发送消息,而不让客户机成为“服务器”,而只使用recv。这可能吗 服务器代码: -

我目前正在编写一个简单的服务器,它将与客户端连接,然后在服务器充当中介的情况下相互交谈

设置为:

  • 服务器启动
  • 2个客户端连接到服务器
  • Client1/2发送具有唯一ID的消息(atom)
  • 服务器将此ID与套接字PID一起保存
  • Client1发送{send_to_id,client2id,Message}
  • 服务器消息客户端2
但这对我不起作用,我得到了一个函数\子句错误

因此,基本上我想用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).
侦听器代码:

-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).
客户端代码:

-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).
你是建议我重写所有的东西还是我的想法可能? 事先谢谢

编辑:添加了一个测试运行

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}]}
Eshell V5.8.5(使用^G中止)
1> imup_服务器:启动(1234)。
#港口
2> Socket1=imup\u客户端:连接(1234)。
接受连接
#港口
3> Socket2=imup\u客户端:连接(1234)。
接受连接
#港口
4> imup_客户端:发送(Socket1,{enter,cOne})。
好啊
获取数据包:={insert_client,#Port,cOne}
收到:[131104,3100,0,13105110115101114116,95,99108105101110,
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_客户端:发送(Socket2,{enter,cTwo})。
好啊
获取数据包:={insert_client,#Port,cTwo}
收到:[131104,3100,0,13105110115101114116,95,99108105101110,
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_客户端:send(Socket1,{send_to_id,cTwo,hello})。
好啊
获取数据包:==7>
=错误报告===2013年5月5日::23:25:49===
进程中存在错误,退出值为:{function_子句,[{gen_tcp,send,[[#Port],]},{imup_侦听器,进程_数据,3},{imup_侦听器,循环,2}}
=错误报告===2013年5月5日::23:25:49===
进程中存在错误,退出值为:{{badmatch,{Error,closed}},[{imup_client,recv,1}]}

因此,在调用带有参数
[#端口]
gen\u tcp:send
时,您会收到一个
function\u子句
错误。第一个参数是包含“端口”(本例中为套接字)的列表,但它应该只是端口


如果我正确地阅读了代码,就会发生这种情况,因为您正在使用
dict:append
将套接字放入字典中,这会导致字典的值是列表。除非您确实需要为每个客户机存储多个套接字,或者反之亦然,否则可能更适合使用
dict:store

因此,调用
gen\u tcp:send
时,会出现
function\u子句
错误,其中包含参数
[\cf#Port]
。第一个参数是包含“端口”(本例中为套接字)的列表,但它应该只是端口


如果我正确地阅读了代码,就会发生这种情况,因为您正在使用
dict:append
将套接字放入字典中,这会导致字典的值是列表。除非您确实需要为每个客户机存储多个套接字,或者反之亦然,否则可能更适合使用
dict:store

为了调试它,最好看到错误消息。。。根据我的经验,function子句通常只是一个拼写错误,或者类似的东西。当然!忘记添加了,它现在被添加到底部。为了调试它,最好看到错误消息。。。根据我的经验,function子句通常只是一个拼写错误,或者类似的东西。当然!忘记添加了,它现在添加到底部。谢谢,我一定是瞎了,因为我甚至测试了dict功能,现在我可以接收消息了。但出现了另一个问题,当服务器接收时,它是二进制的,但当客户端接收时,它变成了一个列表。我不知道为什么会发生这种情况,因为我在两次发送上使用了相同的术语“二进制/二进制”。你知道吗?如果你想以二进制数据的形式接收消息,你的客户端应该以二进制的形式打开套接字。在客户端打开tcp套接字时,将atom二进制文件添加到选项列表中:gen_tcp:connect(IP,PortNo,{binary,{active,false},{packet,2}])谢谢,我一定是瞎了,因为我甚至测试了dict函数,现在我可以接收消息了。但出现了另一个问题,当服务器接收时,它是二进制的,但当客户端接收时,它变成了一个列表。我不知道为什么会发生这种情况,因为我在两次发送上使用了相同的术语“二进制/二进制”。你知道吗?如果你想以二进制数据的形式接收消息,你的客户端应该以二进制的形式打开套接字。在客户端中打开tcp套接字时,将atom二进制文件添加到选项列表中:gen_tcp:connect(IP,PortNo,[binary,{active,false},{packet,2}])