我正在建立与本地主机的套接字连接。我想在此连接(或任何连接)上从服务器接收回输入,但是使用相同的连接似乎是最简单的解决方案。因为几个客户端将能够连接到我的服务器,所以我创建了一个登录功能,该功能将产生一个线程来侦听服务器发送的消息。
当我输入命令
c(erlSoc).
erlSoc:logon("Jacob").
我收到错误消息
[C] error {error,einval}
我知道这一行
E ->
io:format("[C] error ~p~n", [E])
正在打印它,但是我需要更改什么,以便不会发生此错误,并且能够从服务器接收消息。
erlSoc.erl的主要兴趣点
logon(Uname) ->
{ok, Sock} = gen_tcp:connect("localhost", 5300, [binary, {packet, 0}]),
spawn(erlSoc, client_receive, [Sock]),
io:format("Create a Room: create~nList Rooms: list~nJoin Rooms: join~n Leave Rooms: leave~nSend a message: message ~n"),
client(Sock, Uname).
client_receive(Sock) ->
case gen_tcp:recv(Sock, 0) of
{ok, Data} ->
io:format("[C] ~p~n", [Data]);
{error, closed} ->
io:format("[C] closed~n", []);
E ->
io:format("[C] error ~p~n", [E])
end.
erlSoc.erl
-module(erlSoc).
-export([start_server/0, logon/1, remove/2, server/1, client_receive/1]).
-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
listen() ->
{ok, LSocket} = gen_tcp:listen(5300, ?TCP_OPTIONS),
io:format("Accepted the socket connection ~n"),
accept(LSocket).
accept(LSocket) ->
{ok, CSocket} = gen_tcp:accept(LSocket),
Ref = make_ref(),
To = spawn(fun() -> init(Ref, CSocket) end),
gen_tcp:controlling_process(CSocket, To),
To ! {handoff, Ref, CSocket},
io:format("Second Test ~n"),
accept(LSocket).
init(Ref, Socket) ->
receive
{handoff, Ref, Socket} ->
{ok, Peername} = inet:peername(Socket),
io:format("[S] peername ~p~n", [Peername]),
loop(Socket)
end.
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
List = binary_to_list(Data),
List2 = string:tokens(List,","),
io:format("[S] got ~p~n", [List2]),
parse_data(Socket, List2),
loop(Socket);
{error, closed} ->
io:format("[S] closed~n", []);
E ->
io:format("[S] error ~p~n", [E])
end.
server_node() ->
erlSoc@localhost.
parse_data(Soc, [Task, Rname | Message]) ->
if
Task =:= "message" ->
{erlSoc, self()} ! {message, Rname, Message};
Task =:= "create" ->
{erlSoc, server_node()} ! {create, Rname, Soc};
Task =:= "list" ->
{erlSoc, self()} ! {list, Soc};
Task =:= "join" ->
{erlSoc, self()} ! {join, Rname, Soc};
Task =:= "leave" ->
{erlSoc, self()} ! {leave, Rname, Soc}
end.
start_server() ->
spawn(erlSoc, server, [[[]]]),
listen().
remove(X, L) ->
[Y || Y <- L, Y =/= X].
server(RoomList) ->
receive
{message, Rname, Message} ->
[_|[Users|Messages]] = lists:keyfind(Rname, 1, RoomList),
RoomUpdate = lists:keyreplace(Rname, 1, RoomList, [Rname|[Users|[Messages|Message]]]),
send_message(Message, Rname, Users),
server(RoomUpdate);
{create, Rname, Soc} ->
server([[Rname|[Soc|[]]]|RoomList]);
{list, Soc} ->
{Rooms, _} = RoomList,
gen_tcp:send(Soc,Rooms),
server(RoomList);
{join, Rname, User} ->
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomList),
RoomUpdate = lists:keyreplace(Rname, 1, RoomList, [Rname|[[Users|User]|[Messages]]]),
gen_tcp:send(User,[Rname,Messages]),
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomUpdate),
io:format("Users in ~p : ~p~n", [Rname, Users]),
server(RoomList);
{leave, Rname, User} ->
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomList),
NewU = remove(User, Users),
RoomUpdate = lists:keyreplace(Rname, 1, RoomList, [Rname|[[NewU]|[Messages]]]),
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomUpdate),
io:format("Users in ~p : ~p~n", [Rname, Users]),
server(RoomUpdate)
end.
send_message(Message, ChatRoom, []) ->
void;
send_message(Message, Chatroom, [To|Users]) ->
gen_tcp:send(To,"Message From Chatroom "++Chatroom++": "++Message),
send_message(Message, Chatroom, Users).
logon(Uname) ->
{ok, Sock} = gen_tcp:connect("localhost", 5300, [binary, {packet, 0}]),
spawn(erlSoc, client_receive, [Sock]),
io:format("Create a Room: create~nList Rooms: list~nJoin Rooms: join~n Leave Rooms: leave~nSend a message: message ~n"),
client(Sock, Uname).
client_receive(Sock) ->
case gen_tcp:recv(Sock, 0) of
{ok, Data} ->
io:format("[C] ~p~n", [Data]);
{error, closed} ->
io:format("[C] closed~n", []);
E ->
io:format("[C] error ~p~n", [E])
end.
client(Sock, Uname) ->
{ok,[Task]} = io:fread("Task? : ", "~s"),
if
Task =:= "message" ->
{ok, [Rname]} = io:fread("Send the message to which room? : ", "~s"),
Message = io:get_line("Type your message: "),
ok = gen_tcp:send(Sock,"message,"++Rname++","++Uname++": "++Message);
Task =:= "create" ->
{ok, [Rname]} = io:fread("Enter a room name : ", "~s"),
ok = gen_tcp:send(Sock, "create,"++Rname);
Task =:= "list" ->
ok = gen_tcp:send(Sock, "list");
Task =:= "join" ->
{ok, [Rname]} = io:fread("Leave Which Room? : ", "~s"),
ok = gen_tcp:send(Sock, "leave,"++Rname);
Task =:= "leave" ->
{ok, [Rname]} = io:fread("Join Which Room? : ", "~s"),
ok = gen_tcp:send(Sock, "join,"++Rname);
Task =:= "help" ->
io:format("Create a Room: create~nList Rooms: list~nJoin Rooms: join~n Leave Rooms: leave~nSend a message: message ~n");
Task =:= "exit" ->
gen_tcp:close(Sock)
end.
更新
-module(erlSoc).
-export([start_server/0, logon/1, remove/2, server/1, client_receive/1, client/2]).
-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
listen() ->
{ok, LSocket} = gen_tcp:listen(5300, ?TCP_OPTIONS),
io:format("Accepted the socket connection ~n"),
accept(LSocket).
accept(LSocket) ->
{ok, CSocket} = gen_tcp:accept(LSocket),
Ref = make_ref(),
To = spawn(fun() -> init(Ref, CSocket) end),
gen_tcp:controlling_process(CSocket, To),
To ! {handoff, Ref, CSocket},
io:format("Second Test ~n"),
accept(LSocket).
init(Ref, Socket) ->
receive
{handoff, Ref, Socket} ->
{ok, Peername} = inet:peername(Socket),
io:format("[S] peername ~p~n", [Peername]),
loop(Socket)
end.
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
List = binary_to_list(Data),
List2 = string:tokens(List,","),
io:format("[S] got ~p~n", [List2]),
parse_data(Socket, List2),
loop(Socket);
{error, closed} ->
io:format("[S] closed~n", []);
E ->
io:format("[S] error ~p~n", [E])
end.
server_node() ->
erlSoc@localhost.
parse_data(Soc, [Task, Rname | Message]) ->
case Task of
"message" ->
{erlSoc, server_node()} ! {message, Rname, Message};
"create" ->
io:format("[C] ~p~n", [Rname]),
{erlSoc, server_node()} ! {create, Rname, Soc};
"list" ->
{erlSoc, server_node()} ! {list, Soc};
"join" ->
{erlSoc, server_node()} ! {join, Rname, Soc};
"leave" ->
{erlSoc, server_node()} ! {leave, Rname, Soc}
end.
start_server() ->
spawn(erlSoc, server, [[[]]]),
listen().
remove(X, L) ->
[Y || Y <- L, Y =/= X].
server(RoomList) ->
receive
{message, Rname, Message} ->
[_|[Users|Messages]] = lists:keyfind(Rname, 1, RoomList),
RoomUpdate = lists:keyreplace(Rname, 1, RoomList, [Rname|[Users|[Messages|Message]]]),
send_message(Message, Rname, Users),
server(RoomUpdate);
{create, Rname, Soc} ->
server([[Rname|[Soc|[]]]|RoomList]),
io:format("[C] ~p~n", [Rname]);
{list, Soc} ->
{Rooms, _} = RoomList,
gen_tcp:send(Soc,Rooms),
server(RoomList);
{join, Rname, User} ->
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomList),
RoomUpdate = lists:keyreplace(Rname, 1, RoomList, [Rname|[[Users|User]|[Messages]]]),
gen_tcp:send(User,[Rname,Messages]),
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomUpdate),
io:format("Users in ~p : ~p~n", [Rname, Users]),
server(RoomList);
{leave, Rname, User} ->
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomList),
NewU = remove(User, Users),
RoomUpdate = lists:keyreplace(Rname, 1, RoomList, [Rname|[[NewU]|[Messages]]]),
[Room|[Users|Messages]] = lists:keyfind(Rname, 1, RoomUpdate),
io:format("Users in ~p : ~p~n", [Rname, Users]),
server(RoomUpdate)
end.
send_message(Message, ChatRoom, []) ->
void;
send_message(Message, Chatroom, [To|Users]) ->
gen_tcp:send(To,"Message From Chatroom "++Chatroom++": "++Message),
send_message(Message, Chatroom, Users).
logon(Uname) ->
{ok, Sock} = gen_tcp:connect("localhost", 5300, [binary, {packet, 0}, {active, false}]),
Merp = spawn(erlSoc, client_receive, [Sock]),
gen_tcp:controlling_process(Sock, Merp),
io:format("Create a Room: create~nList Rooms: list~nJoin Rooms: join~n Leave Rooms: leave~nSend a message: message ~n"),
Raph = spawn(erlSoc, client, [Sock, Uname]),
gen_tcp:controlling_process(Sock, Raph).
client_receive(Sock) ->
case gen_tcp:recv(Sock, 0) of
{ok, Data} ->
io:format("[C] ~p~n", [Data]),
client_receive(Sock);
{error, closed} ->
io:format("[C] closed~n", []);
E ->
io:format("[C] error ~p~n", [E])
end.
client(Sock, Uname) ->
{ok,[Task]} = io:fread("Task? : ", "~s"),
if
Task =:= "message" ->
{ok, [Rname]} = io:fread("Send the message to which room? : ", "~s"),
Message = io:get_line("Type your message: "),
ok = gen_tcp:send(Sock,"message,"++Rname++","++Uname++": "++Message);
Task =:= "create" ->
{ok, [Rname]} = io:fread("Enter a room name : ", "~s"),
ok = gen_tcp:send(Sock, "create,"++Rname);
Task =:= "list" ->
ok = gen_tcp:send(Sock, "list");
Task =:= "join" ->
{ok, [Rname]} = io:fread("Leave Which Room? : ", "~s"),
ok = gen_tcp:send(Sock, "leave,"++Rname);
Task =:= "leave" ->
{ok, [Rname]} = io:fread("Join Which Room? : ", "~s"),
ok = gen_tcp:send(Sock, "join,"++Rname);
Task =:= "help" ->
io:format("Create a Room: create~nList Rooms: list~nJoin Rooms: join~n Leave Rooms: leave~nSend a message: message ~n");
Task =:= "exit" ->
gen_tcp:close(Sock)
end,
client(Sock,Uname).
答案 0 :(得分:2)
自从您标记了functional-programming
后,我将不仅仅向您介绍我在代码中发现的错误,还可以告诉您更多的信息。
我已经在您的代码中添加了一些注释:
erlSoc.erl
-module(erlSoc).
-export([start_server/0, logon/1, remove/2, server/1, client_receive/1]).
-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
% ...
loop(Socket) ->
% Remember that in server, after accepting a connection,
% you are waiting for packet
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
% ...
parse_data(Soc, [Task, Rname | Message]) ->
if
Task =:= "message" ->
% below code does not work. If you want to send a message to a process,
% you can use its process id (pid) or its registered name or name of the
% node which that process belongs to with its pid
% Based server/1 function, I think that you want to have a process named
% erlSoc and send message to it. If yes, then you have to register that
% process for that name using erlang:register/2 and just use:
% erlSoc ! YourMessage
{erlSoc, self()} ! {message, Rname, Message};
% ...
Task =:= "list" ->
% It seems that you are sending a socket to your erlSoc process and you
% did not make erlSoc controller of socket !
{erlSoc, self()} ! {list, Soc};
logon(Uname) ->
% In below, Your socket will be in 'active' mode and in client_receive/2 function
% you have called gen_tcp:recv/2 on socket which is invalid (einval)
{ok, Sock} = gen_tcp:connect("localhost", 5300, [binary, {packet, 0}]),
% In below, You are passing Sock to a new process which is not owner of socket
% You have to call gen_tcp:controlling_process/2 here
spawn(erlSoc, client_receive, [Sock]),
io:format("Create a Room: create~nList Rooms: list~nJoin Rooms: join~n Leave Rooms: leave~nSend a message: message ~n"),
% Also here you need to call gen_tcp:controlling_process/2 in that process to
% become owner of socket in this process
client(Sock, Uname).
client_receive(Sock) ->
% As I mentioned, In server you were waiting for a packet, Also here in
% client you are waiting for a packet ! So nothing will happen
% If you want to use gen_tcp:recv, I recommend to use it with third
% argument which is timeout
case gen_tcp:recv(Sock, 0) of
{ok, Data} ->
io:format("[C] ~p~n", [Data]);
{error, closed} ->
io:format("[C] closed~n", []);
E ->
io:format("[C] error ~p~n", [E])
end.
% ...
我建议重写部分代码。最好将客户端和服务器代码分开。我也建议正确命名事物,在Erlang中,模块和函数名称均应小写。推荐。您可以使用if
来代替case
,例如:
if
Task =:= "message" ->
{erlSoc, self()} ! {message, Rname, Message};
Task =:= "create" ->
{erlSoc, server_node()} ! {create, Rname, Soc};
Task =:= "list" ->
{erlSoc, self()} ! {list, Soc};
Task =:= "join" ->
{erlSoc, self()} ! {join, Rname, Soc};
Task =:= "leave" ->
{erlSoc, self()} ! {leave, Rname, Soc}
end.
可以是:
case Task of
"message" ->
{erlSoc, self()} ! {message, Rname, Message};
"create" ->
{erlSoc, server_node()} ! {create, Rname, Soc};
"list" ->
{erlSoc, self()} ! {list, Soc};
"join" ->
{erlSoc, self()} ! {join, Rname, Soc};
"leave" ->
{erlSoc, self()} ! {leave, Rname, Soc}
end.