Erlang: подключение к серверу и получение ввода обратно в тот же сокет - PullRequest
0 голосов
/ 15 декабря 2018

Я устанавливаю сокет-соединение с локальным хостом.Я хотел бы получить входные данные от сервера по этому соединению (или по любому соединению, но использование того же соединения казалось самым простым решением).Поскольку несколько клиентов смогут подключаться к моему серверу, я создал функцию входа в систему, которая, помимо прочего, будет создавать поток, который прослушивает сообщения, отправленные с сервера.

Когда я ввожу команды

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).

1 Ответ

0 голосов
/ 15 декабря 2018

Поскольку вы пометили 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.
...