diff options
author | Micael Karlberg <[email protected]> | 2018-06-07 17:50:38 +0200 |
---|---|---|
committer | Micael Karlberg <[email protected]> | 2018-09-18 13:01:37 +0200 |
commit | 70b74f57a360c2b2a1edfe46c61d2ea170d73f91 (patch) | |
tree | afb65cc899696b45465dd4255373a0eb9598a5ad /erts/doc | |
parent | 9e0acc8f442549f1f5ee4271816cbfbeb25d8719 (diff) | |
download | otp-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')
-rw-r--r-- | erts/doc/src/socket.xml | 250 |
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> |