aboutsummaryrefslogtreecommitdiffstats
path: root/erts/doc/src
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-06-07 17:50:38 +0200
committerMicael Karlberg <[email protected]>2018-09-18 13:01:37 +0200
commit70b74f57a360c2b2a1edfe46c61d2ea170d73f91 (patch)
treeafb65cc899696b45465dd4255373a0eb9598a5ad /erts/doc/src
parent9e0acc8f442549f1f5ee4271816cbfbeb25d8719 (diff)
downloadotp-70b74f57a360c2b2a1edfe46c61d2ea170d73f91.tar.gz
otp-70b74f57a360c2b2a1edfe46c61d2ea170d73f91.tar.bz2
otp-70b74f57a360c2b2a1edfe46c61d2ea170d73f91.zip
[socket-nif-doc] More polishing
Also added (a very) temporary example. OTP-14831
Diffstat (limited to 'erts/doc/src')
-rw-r--r--erts/doc/src/socket.xml250
1 files changed, 243 insertions, 7 deletions
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index d0a316c4de..3efa412b8a 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -149,7 +149,17 @@
<name name="close" arity="1"/>
<fsummary>Close a socket.</fsummary>
<desc>
- <p>Closes the socket.</p>
+ <p>Closes the socket.</p>
+
+ <note>
+ <p>Note that for e.g. <c>protocol</c> = <c>tcp</c>, most implementations
+ doing a close does not guarantee that any data sent is delivered to
+ the recipient before the close is detected at the remote side. </p>
+ <p>One way to handle this is to use the
+ <seealso marker="#shutdown/2"><c>shutdown</c></seealso> function
+ (<c>socket:shutdown(Socket, write)</c>) to signal that no more data is
+ to be sent and then wait for the read side of the socket to be closed.</p>
+ </note>
</desc>
</func>
@@ -159,7 +169,7 @@
<fsummary>Initiate a connection on a socket.</fsummary>
<desc>
<p>This function connects the socket to the address
- specied by the <c>Addr</c> argument.</p>
+ specied by the <c>SockAddr</c> argument.</p>
</desc>
</func>
@@ -171,12 +181,27 @@
<name name="getopt" arity="3" clause_i="5"/>
<name name="getopt" arity="3" clause_i="6"/>
<name name="getopt" arity="3" clause_i="7"/>
+ <fsummary>Get an option on a socket.</fsummary>
+ <desc>
+ <p>Get an option on a socket.</p>
+ <p>What properties are valid depend both on <c>Level</c> and
+ on what kind of socket it is (<c>domain</c>, <c>type</c> and
+ <c>protocol</c>).</p>
+
+ <note><p>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.</p></note>
+ </desc>
+ </func>
+
+ <func>
<name name="getopt" arity="3" clause_i="8"/>
<fsummary>Get an option on a socket.</fsummary>
<desc>
<p>Get an option on a socket.</p>
- <p>What properties are valid depend on what kind of socket
- it is (<c>domain</c>, <c>type</c> and <c>protocol</c>).</p>
+ <p>What properties are valid depend both on <c>Level</c> and
+ on what kind of socket it is (<c>domain</c>, <c>type</c> and
+ <c>protocol</c>).</p>
<p>When specifying <c>Level</c> as an integer, and therefor
using "native mode", it is *currently* up to the caller to
know how to interpret the result.</p>
@@ -241,12 +266,12 @@
<p>Receive a message from a socket.</p>
<p>This function reads "messages", which means that regardless of
how much we want to read, it returns when we get a message.</p>
- <p>The <c>MaxSize</c> argument basically defines the size of the
+ <p>The <c>BufSz</c> argument basically defines the size of the
receive buffer. By setting the value to zero (0), the configured
- size (setopt) is used.</p>
+ size (setopt with <c>Level</c> = <c>otp</c>) is used.</p>
<p>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*
+ (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.</p>
</desc>
@@ -305,5 +330,216 @@
</func>
</funcs>
+ <section>
+ <title>Examples</title>
+ <marker id="examples"></marker>
+ <p>TBD: Need to implement a receiver process in order to be able to
+ implement active!</p>
+ <p>x_tcp.erl:</p>
+ <code type="none">
+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.
+ </code>
+
+ <code type="none">
+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.
+ </code>
+
+ <code type="none">
+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.
+ </code>
+
+ <code type="none">
+send(Socket, Data) ->
+ socket:send(Socket, Data).
+ </code>
+
+ <code type="none">
+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).
+ </code>
+
+ <p>The receiver process: </p>
+ <code type="none">
+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.
+ </code>
+
+ </section>
</erlref>