【问题标题】:Communicating Erlang server with c# program与 C# 程序通信 Erlang 服务器
【发布时间】:2016-03-22 22:47:15
【问题描述】:

我正在尝试为我的程序编写 Erlang 服务器,但仍有一些我无法处理的问题。使用时(我用 erlang 编写客户端来测试服务器):

send(Socket, Message) ->
BinMsg = term_to_binary(Message),
gen_tcp:send(Socket, Message).

一切似乎都还好,但是当我尝试从我的 c# 客户端发送内容时,服务器会看到连接和断开连接但没有看到消息(它甚至没有反应)

theStream = mySocket.GetStream();
theWriter = new StreamWriter(theStream);
String foo = theLine + "\r\n"; 
test = System.Text.Encoding.UTF8.GetBytes(foo);
theWriter.Write(test);

我意识到这可能是 Erlang 的一些问题

term_to_binary()

但我不知道如何解决它。我需要简单的:

  1. 客户端将数据转换为字节
  2. 客户端向服务器发送数据
  3. 服务器对数据进行编码
  4. 服务器决定做什么
  5. 服务器生成新数据,将其转换为字节并发送给客户端
  6. 客户端对其进行编码

我可以简单地处理字符串,但我认为这种方法不是很好的解决方案,我想发送二进制文件而不是字符串。有什么建议吗?

【问题讨论】:

    标签: c# erlang server binaries


    【解决方案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:store(Socket, Alias, Clients),
          TmpClients2 = dict:store(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.
    

    听众:

    -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("wchodze pakiet"),
          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("wchodze closed"),
          io:format("Socket ~p closed~n", [Socket]);
        {tcp_error, Socket, Reason} ->
          io:format("wchodze error"),
          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)).
    

    Erlang 客户端(工作):

    connect(PortNo) ->
      {ok, Socket} = gen_tcp:connect("localhost", PortNo, [binary, {active, false}, {packet, 2}]),
      spawn(fun() -> recv(Socket) end),
      Socket.
    
    connect(IP, PortNo) ->
      {ok, Socket} = gen_tcp:connect(IP, PortNo, [binary, {active, false}, {packet, 2}]),
      spawn(fun() -> recv(Socket) end),
      Socket.
    
    
    send(Socket, Message) ->
      BinMsg = unicode:characters_to_binary(Message),
      io:format(Message ++ "~n"),
      io:format(BinMsg),
      gen_tcp:send(Socket, BinMsg).
    
    
    recv(Socket) ->
      {ok, A} = gen_tcp:recv(Socket, 0),
      io:format("Received: ~p~n", [A]),
      recv(Socket).
    
    
    disconnect(Socket) ->
      gen_tcp:close(Socket).
    

    C# 客户端:

    using UnityEngine;
    using System;
    using System.IO;
    using System.Net.Sockets;
    
    //using System.Globalization.NumberStyles;
    
    
    
    public class NetworkController : MonoBehaviour
    
    {
    
        public string tekst;
    
        public Transform nek;
    
        public byte[] bytesW = new byte[4];
        public byte[] test;
    
        public float[] qq = new float[4];
    
        internal Boolean socketReady = false;
    
        TcpClient mySocket;
    
        NetworkStream theStream;
    
        StreamWriter theWriter;
    
        BStreamReader theReader;
    
        String Host = "localhost";
    
        public Int32 Port;
    
    
    
        void Update()
    
        {
    
            string receivedText = readSocket();
    
            if (receivedText != "")
    
            {
                tekst = receivedText;
            }
    
        }
    
    
    
        void OnGUI()
    
        {
            if (!socketReady)
    
            {
                if (GUILayout.Button("Connect"))
                {
                    setupSocket();
                    writeSocket("serverStatus:");
                }
            }
             if (GUILayout.Button("Send"))
             {
                 writeSocket("ala");
             }
             if (GUILayout.Button("Close"))
             {
                 closeSocket();
             }
    
            GUI.Label(new Rect(0, 40, 1000, 400), tekst);
            if (socketReady)
            {
    
    
            }
        }
    
        void OnApplicationQuit()
    
        {
    
            closeSocket();
    
        }
    
    
    
        public void setupSocket()
    
        {
    
            try
    
            {
    
    
                mySocket = new TcpClient(Host, Port);
    
                theStream = mySocket.GetStream();
    
                theWriter = new StreamWriter(theStream);
    
                theReader = new StreamReader(theStream);
    
                socketReady = true;
    
            }
    
    
    
            catch (Exception e)
    
            {
    
                Debug.Log("Socket error: " + e);
    
            }
    
        }
    
    
    
        public void writeSocket(string theLine)
    
        {
    
            if (!socketReady)
    
                return;
    
            //byte[] foo = System.Text.Encoding.UTF8.GetBytes(theLine);
            String foo = theLine + "\r\n"; 
            Debug.Log(foo);
            test = System.Text.Encoding.UTF8.GetBytes(foo);
    
    
    
            theWriter.Write(test);
            theWriter.Flush();
    
        }
    /*
        public void WriteToStream(string theLine)
        {
            byte[] len_bytes = BitConverter.GetBytes(length);
    
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(len_bytes);
            }
            writer.Write(len_bytes);
    
            writer.Write(content);
        }
        */
    
    
    
        public String readSocket()
    
        {
    
            if (!socketReady)
    
                return "";
    
            if (theStream.DataAvailable)
    
                return theReader.ReadLine();
    
            //return theReader.ReadToEnd();
    
            return "";
    
        }
    
    
    
        public void closeSocket()
    
        {
    
            if (!socketReady)
    
                return;
    
            theWriter.Close();
    
            theReader.Close();
    
            mySocket.Close();
    
            socketReady = false;
    
        }
    }
    

    它还没有准备好,但它应该发送基本消息,但它没有。正如我所说,我对 Erlang 很陌生,所以也许我做了一些愚蠢的事情

    【讨论】:

    • 我不知道你的 erlang 客户端实际上是如何工作的,因为我可以在服务器端看到 binary_to_term 而在客户端我看不到 term_to_binary。此外,您使用{packet, 2},这意味着 Erlang 期望数据包的前两个字节是数据包长度,然后它等待length 字节,然后剥离 2 字节标头,然后传递消息。顺便说一句,您没有使用 gen_server 和其他 OTP 做法。抱歉,您最好从learnyousomeerlang.com/content开始
    【解决方案2】:

    您应该在客户端正确编码数据,以便能够在 Erlang 中使用term_to_binary/binary_to_term。看看http://erlang.org/doc/apps/erts/erl_ext_dist.html,实现起来并不难,我认为应该有现成的解决方案。

    但从你的话来看,你的代码似乎还有其他问题,因为 Erlang 应该在错误的二进制文件上抛出异常。可能与{active, true|false|once|N} 有关。您应该在套接字上设置{active, true} 以便能够接收 TCP 数据包作为 Erlang 消息(onceN 也很好,请查看文档:http://erlang.org/doc/man/gen_tcp.html)。如果你想接收二进制文件,你可能应该在套接字上设置binary 选项。

    【讨论】:

    • 感谢您的链接,但我已经阅读了它并且(可能是因为我对 Erlang 还很陌生)它对我没有帮助。正如我所写,当我使用用 erlang 编写的客户端时,一切似乎都很好,只有在使用 c# 的客户端时我才会遇到麻烦。服务器不会引发异常原因,正如我测试的那样,它看起来甚至没有收到消息。那我可以在不编码/编码的情况下编写它吗?会不会很糟糕/不专业?
    • 无论如何你都需要一些序列化格式。 term_to_binary 就像,我不知道,JSON.serialize。因此,如果您在 Erlang 客户端中使用 term_to_binary,在 Erlang 服务器中使用 binary_to_term,您可能应该在 C# 端使用与 erlang 兼容的序列化。也许你可以给我们看一下erlang服务器和客户端的代码?
    • 顺便说一下,如果有疑问,请使用 Wireshark!
    猜你喜欢
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 2017-01-04
    相关资源
    最近更新 更多