Reputation: 71
I'm trying to write Erlang server for my program but still having some problem which I can't handle with. When using this (I wrote client in erlang to test server):
send(Socket, Message) ->
BinMsg = term_to_binary(Message),
gen_tcp:send(Socket, Message).
everything seems to be ok, but when I'm trying to send something from my c# client, server sees connection and dissconnection but doesn't see message (it's even not reacting)
theStream = mySocket.GetStream();
theWriter = new StreamWriter(theStream);
String foo = theLine + "\r\n";
test = System.Text.Encoding.UTF8.GetBytes(foo);
theWriter.Write(test);
I realised that it's probably some issue with Erlang's
term_to_binary()
but I don't know how to fix it. I need simple:
And I can do it simple on strings, but I'dont think this method will be good solution and I want to send binaries instead of strings. Any suggetions?
Upvotes: 3
Views: 1314
Reputation: 71
So here's my server/client code:
-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.
Listener:
-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 CLient (working):
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# Client:
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;
}
}
It's not ready yet but it should send basic message and it doesn't. As i told, I'm pretty new to Erlang so maybe I did something stupid
Upvotes: 0
Reputation: 1156
You should properly encode data on client side in order to be able to use term_to_binary/binary_to_term
in Erlang. Take a look at http://erlang.org/doc/apps/erts/erl_ext_dist.html it is not that hard to implement and I think there are should be existing solutions.
But from your words it looks like something else wrong with your code since Erlang should throw exception on wrong binaries. Probably something to do with {active, true|false|once|N}
. You should set {active, true}
on socket to be able to receive TCP packets as Erlang messages (once
or N
are good too, take a look into docs: http://erlang.org/doc/man/gen_tcp.html). And if you want to receive binaries you should probably set binary
option on socket.
Upvotes: 1