我目前正在编写一个简单的服务器,它可以与客户端连接,然后与服务器充当中介的彼此交谈。
设置为:
但这对我不起作用,我得到一个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).
你建议我重写一切,还是我的想法有可能? 先谢谢!
编辑:添加了一个测试。
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 :(得分:4)
因此,在使用参数function_clause
和gen_tcp:send
调用[#Port<0.684>]
时,您收到<<9 bytes>>
错误。第一个参数是一个包含“port”的列表(在这种情况下是一个套接字),但它应该只是端口。
如果我正确读取代码,则会发生这种情况,因为您将套接字放入带有dict:append
的字典中,这会导致字典的值成为列表。除非您确实需要为每个客户端存储多个套接字,否则可能dict:store
更适合。