This module provides an API for the socket interface. It is used to create, delete and manipulate sockets.
As returned by
Accept a connection on a socket.
This call is used with connection-based socket types
(
Bind a name to a socket.
When a socket is created
(with
The rules used for name binding vary between domains.
Closes the socket.
Note that for e.g.
One way to handle this is to use the
This function connects the socket to the address
specied by the
Get an option on a socket.
What properties are valid depend both on
Not all options are valid on all platforms. That is, even if "we" support an option, that does not mean that the underlying OS does.
Get an option on a socket.
What properties are valid depend both on
When specifying
Not all options are valid on all platforms. That is, even if "we" support an option, that does not mean that the underlying OS does.
Listen for connections on a socket.
Creates an endpoint (socket) for communication.
For some
The
Returns the address of the peer connected to the socket.
Receive a message from a socket.
There is a special case for the argument
Receive a message from a socket.
This function reads "messages", which means that regardless of how much we want to read, it returns when we get a message.
The
It may be impossible to know what (buffer) size is appropriate "in advance", and in those cases it may be convenient to use the (recv) 'peek' flag. When this flag is provided, the message is *not* "consumed" from the underlying buffers, so another recvfrom call is needed, possibly with a then adjusted buffer size.
Send a message on a connected socket.
Send a message on a socket, to the specified destination.
Set options on a socket.
What properties are valid depend both on
Not all options are valid on all platforms. That is, even if "we" support an option, that does not mean that the underlying OS does.
Sockets are set 'non-blocking' when created, so this option is *not* available (as it would adversely effect the Erlang VM to set a socket 'blocking').
Shut down all or part of a full-duplex connection.
Returns the current address to which the socket is bound.
TBD: Need to implement a receiver process in order to be able to implement active!
x_tcp.erl:
listen(Addr, Port) ->
try
begin
Socket = case socket:open(inet, stream, tcp) of
{ok, Socket} ->
Socket;
{error, _} = OERROR ->
throw(OERROR)
end,
SockAddr = #in4_sockaddr{port = Port,
addr = Addr},
ok = case socket:bind(Socket, SockAddr) of
ok ->
ok;
{error, _} = BERROR ->
throw(BERROR)
end,
case socket:listen(Socket, 10) of
ok ->
{ok, Socket};
{error, _} = LERROR ->
throw(LERROR)
end
end
catch
throw:ERROR ->
ERROR
end.
connect(Addr, Port) ->
try
begin
Socket = case socket:open(inet, stream, tcp) of
{ok, Socket} ->
Socket;
{error, _} = OERROR ->
throw(OERROR)
end,
BSockAddr = #in4_sockaddr{port = 0,
addr = any},
ok = case socket:bind(Socket, BSockAddr) of
ok ->
ok;
{error, _} = BERROR ->
throw(BERROR)
end,
CSockAddr = #in4_sockaddr{port = Port,
addr = Addr},
Socket = case socket:connect(Socket, CSockAddr) of
{ok, Sock} ->
Sock;
{error, _} = CERROR ->
throw(CERROR)
end,
case start_receiver(Socket) of
{ok, Pid} ->
ok = socket:ts_add(Socket, receiver, Pid),
{ok, Socket};
{error, _} = RERROR ->
socket:close(Socket),
throw(RERROR)
end
end
catch
throw:ERROR ->
ERROR
end.
accept(LSocket) ->
case socket:accept(LSocket) of
{ok, Socket} ->
case start_receiver(Socket) of
{ok, Pid} ->
ok = socket:ts_add(Socket, receiver, Pid),
{ok, Socket};
{error, _} = RERROR ->
socket:close(Socket),
throw(RERROR)
end,
{error, _} = AERROR ->
AERROR
end.
send(Socket, Data) ->
socket:send(Socket, Data).
setopt(Socket, controlling_process = Item, Pid) when is_pid(Pid) ->
case socket:setopt(Socket, otp, Item, Pid) of
ok ->
{ok, Receiver} = socket:ts_get(Socket, receiver),
update_receiver(Receiver, {controlling_process, Pid),
ok;
{error, _} = ERROR ->
ERROR
end;
setopt(Socket, active, Active) ->
{ok, Receiver} = socket:ts_get(Socket, receiver),
receiver_active(Receiver, Active),
ok;
%% This is just to indicate that there will be more options
%% that needs to be handled
setopt(Socket, Item, Pid) when is_pid(Pid) ->
socket:setopt(Socket, which_level(Item), Item, Pid).
The receiver process:
start_receiver(Socket) ->
CtrlProc = self(),
Ref = make_ref(),
Receiver = proc_lib:spawn_link(fun() -> receiver_init(CtrlProc, Ref) end),
receive
{?MODULE, started, Ref} ->
Receiver ! {?MODULE, receiver, Ref, Sock, true},
unlink(Receiver),
{ok, Receiver};
{'EXIT', Receiver, Reason} ->
{error, Reason}
end.
receiver_active(Receiver, Active)
when (Active =:= false) orelse
(Active =:= true) orelse
(Active =:= once) orelse
is_integer(Active) ->
Receiver ! {?MODULE, active, What}.
receiver_update(Receiver, What) ->
Ref = make_ref(),
Receiver ! {?MODULE, receiver_update, Ref, What},
receive
{?MODULE, receiver_upadate, Ref, Result} ->
Result
end.
-record(receiver, {sock :: socket:socket(),
ctrl :: pid(),
num_packages :: infinity | non_neg_ineteger()}).
receiver_init(Pid, Ref) ->
receive
{?MODULE, receiver, Ref, stop} ->
i("received stop"),
exit(normal);
{?MODULE, receiver, Ref, Sock, InitialMode} ->
i("received socket: ~p", [Sock]),
NumPackages = mode2pkgcount(InitialMode),
receiver(#receiver{sock = Sock, ctrl = Pid})
end.
mode2pkgcount(true) ->
infinity;
mode2pkgcount(false) ->
0;
mode2pkgcount(N) when is_integer(N) andalso (N >= 0) ->
N.
receiver(#receiver{num_packages = 0} = State) ->
receive
{?MODULE, active, false} ->
receiver(State);
{?MODULE, active, true} ->
receiver(State#receiver{num_packages = infinity});
{?MODULE, active, once} ->
receiver(State#receiver{num_packages = 1});
{?MODULE, active, N} when (N > 0) ->
receiver(State#receiver{num_packages = N})
end;
receiver(#receiver{num_packages = N, sock = Sock, ctrl = Pid} = State) ->
case socket:recv(Sock, 0) of
{ok, Package} when size(Package) > 0 ->
Pid ! {tcp, Sock, Packege},
case next_active(N) of
0 ->
Pid ! {tcp_passive, Sock},
receiver(State#{num_packages = 0});
NextN ->
receiver(State#{num_packages = NextN})
end;
{ok, Package} when size(Package) =:= 0 ->
receiver(State);
{error, closed} ->
i("closed"),
Pid ! {tcp_closed, Sock},
exit(normal);
{error, Reason} ->
i("error: ~p", [Reason]),
Pid ! {tcp_error, Sock, Reason},
exit(normal)
end.