Erlang/OTP Forums

Author Message

<  Erlang  ~  Sockets question

alanw
Posted: Fri Nov 21, 2008 2:43 am Reply with quote
Joined: 20 Nov 2008 Posts: 3
Hi,

I'm new to Erlang, but pretty excited about using it for a communications project. I need to reverse engineer a protocol and wrote a simple program that sites between the client and server to dump the traffic. The single-process version works fine, but the multi-process version doesn't work and I'm stumped. It seems like there'd be a "canonical" way to do this and I'd appreciate your help. The dumpBytes/1 call prints the buffer in a special way, I haven't included it as it has several hundred lines of code (already tested)

1) Here's my sequential code:
Code:

-module(spy).
-author('Alan Walker').

-export([listen/1]).

-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
-define(CLIENT_OPTIONS, [binary, {packet, 0}, {active, false},
          {nodelay, true}]).

% Call echo:listen(Port) to start the service.
listen(Port) ->
    {ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
    accept(LSocket).

% Wait for incoming connections and spawn the echo loop when we get one.
accept(LSocket) ->
    {ok, Socket} = gen_tcp:accept(LSocket),
    io:format("got a client~n"),
    spawn(fun() -> setup(Socket) end),
    accept(LSocket).

% Setup the pass-thru
setup(ClientSock) ->
    {ok, ServerSock} = gen_tcp:connect("172.30.40.254", 30030, ?CLIENT_OPTIONS, 5000),
    noLoop(ClientSock, ServerSock).

noLoop(ClientSock, ServerSock) ->
    case gen_tcp:recv(ClientSock, 0) of
        {ok, Data} ->
            io:format("Received (~s)~n", ["Client"]),
            gen_tcp:send(ServerSock, Data),
            io:format("Forwarded (~s)~n", ["Client"]),
            hssp:dumpBytes(Data);
        {error, Reason} ->
            io:format("***** Error: ~s~n~w~n", ["Client", Reason]),
            ok
    end,

    case gen_tcp:recv(ServerSock, 0) of
        {ok, Data2} ->
            io:format("Received (~s)~n", ["Server"]),
            gen_tcp:send(ClientSock, Data2),
            io:format("Forwarded (~s)~n", ["Server"]),
            hssp:dumpBytes(Data2),
            noLoop(ClientSock, ServerSock);
        {error, Reason2} ->
            io:format("***** Error: ~s~n~w~n", ["Server", Reason2]),
            ok
    end.


2) ***** Here's my attempt at having two processes, one to pass data in each direction. The protocol I'm spying on is truly bi-directional and will have occasions where multiple messages flow one way before a reply occurs. The server immediately closes the socket, but I can't figure out why (I don't have the source).

Code:

% Setup the pass-thru, one process each way
setup(ClientSock) ->
    {ok, ServerSock} = gen_tcp:connect("172.30.40.254", 30030, ?CLIENT_OPTIONS, 5000),
    spawn(fun() -> loop(ClientSock, ServerSock, "From Client") end),
    spawn(fun() -> loop(ServerSock, ClientSock, "From Server") end).

% Pass through and print the data between the sockets.
loop(SockIn, SockOut, Title) ->
    case gen_tcp:recv(SockIn, 0) of
        {ok, Data} ->
            io:format("Received (~s)~n", [Title]),
            gen_tcp:send(SockOut, Data),
            io:format("Forwarded (~s)~n", [Title]),
            hssp:dumpBytes(Data),
            loop(SockIn, SockOut, Title);
        {error, Reason} ->
            io:format("***** Error: ~s~n~w~n", [Title, Reason]),
            ok
    end.
View user's profile Send private message
chaoslawful
Posted: Tue Nov 25, 2008 7:58 am Reply with quote
Joined: 25 Nov 2008 Posts: 2 Location: Beijing, China
alanw wrote:

...
Code:

% Setup the pass-thru, one process each way
setup(ClientSock) ->
    {ok, ServerSock} = gen_tcp:connect("172.30.40.254", 30030, ?CLIENT_OPTIONS, 5000),
    spawn(fun() -> loop(ClientSock, ServerSock, "From Client") end),
    spawn(fun() -> loop(ServerSock, ClientSock, "From Server") end).

% Pass through and print the data between the sockets.
loop(SockIn, SockOut, Title) ->
    case gen_tcp:recv(SockIn, 0) of
        {ok, Data} ->
            io:format("Received (~s)~n", [Title]),
            gen_tcp:send(SockOut, Data),
            io:format("Forwarded (~s)~n", [Title]),
            hssp:dumpBytes(Data),
            loop(SockIn, SockOut, Title);
        {error, Reason} ->
            io:format("***** Error: ~s~n~w~n", [Title, Reason]),
            ok
    end.


It's because a gen_tcp socket is actually a port, so the socket's lifespan is exactly the same as the process which created it.

In your code, the setup process terminated after created ServerSock and spawned two processes, so the ServerSock was closed initiately before loop process can do anything useful.

gen_tcp:controlling_process() can be used to change the socket's owner process, so the socket's lifespan will be altered too. The program should work if you change the definition of setup() to the following code:
Code:

% Setup the pass-thru, one process each way
setup(ClientSock) ->
    {ok, ServerSock} = gen_tcp:connect("172.30.40.254", 30030, ?CLIENT_OPTIONS, 5000),
    spawn(fun() -> loop(ClientSock, ServerSock, "From Client") end),
    gen_tcp:controlling_process(ServerSock,spawn(fun() -> loop(ServerSock, ClientSock, "From Server") end)).
View user's profile Send private message
alanw
Posted: Sun Nov 30, 2008 3:41 am Reply with quote
Joined: 20 Nov 2008 Posts: 3
Thanks for your note. This makes sense, my problem is now fixed.

I am still trying to un-learn all my habits from many years of writing threaded code.

Alan
View user's profile Send private message

Display posts from previous:  

All times are GMT
Page 1 of 1
This forum is locked: you cannot post, reply to, or edit topics.

Jump to:  

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum