aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
authorErlang/OTP <otp@erlang.org>2010-02-08 15:56:55 +0000
committerErlang/OTP <otp@erlang.org>2010-02-08 15:56:55 +0000
commit51bb4c6ae1e4d61304aee5b4bf77279d838da84a (patch)
tree8ffeb120513b2e1bc4de8e32560d3956e67cfd82 /lib/kernel
parent52f763526c95a7eb12fcfbdf07bb1216a1b756b4 (diff)
parent827bb85bafd96ec3037c849ea42878e4f581d22f (diff)
downloadotp-51bb4c6ae1e4d61304aee5b4bf77279d838da84a.tar.gz
otp-51bb4c6ae1e4d61304aee5b4bf77279d838da84a.tar.bz2
otp-51bb4c6ae1e4d61304aee5b4bf77279d838da84a.zip
Merge branch 'sc/sctp-connect-nowait' into ccase/r13b04_dev
* sc/sctp-connect-nowait: Implement a non-blocking SCTP connect OTP-8414 There are new gen_sctp:connect_init/* functions that initiate an SCTP connection without blocking for the result. The result is delivered asynchronously as an sctp_assoc_change event. (Thanks to Simon Cornish.)
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/gen_sctp.xml119
-rw-r--r--lib/kernel/src/gen_sctp.erl46
-rw-r--r--lib/kernel/src/inet_sctp.erl30
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl62
4 files changed, 220 insertions, 37 deletions
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index de41178a17..3a8011e28b 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2007</year><year>2009</year>
+ <year>2007</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>gen_sctp</title>
@@ -170,10 +170,19 @@
<p>Establishes a new association for the socket <c>Socket</c>,
with the peer (SCTP server socket) given by
<c>Addr</c> and <c>Port</c>. The <c>Timeout</c>,
- is expressed in milliseconds.</p>
- <p>A socket can be associated with multiple peers.
- <marker id="record-sctp_assoc_change"></marker>
+ is expressed in milliseconds. A socket can be associated with multiple peers.</p>
+ <p><b>WARNING:</b>Using a value of <c>Timeout</c> less than
+ the maximum time taken by the OS to establish an association (around 4.5 minutes
+ if the default values from RFC 4960 are used) can result in
+ inconsistent or incorrect return values. This is especially
+ relevant for associations sharing the same <c>Socket</c>
+ (i.e. source address and port) since the controlling process
+ blocks until <c>connect/*</c> returns.
+ <seealso marker="#connect_init/4">connect_init/*</seealso>
+ provides an alternative not subject to this limitation.</p>
+
+ <p><marker id="record-sctp_assoc_change"></marker>
The result of <c>connect/*</c> is an <c>#sctp_assoc_change{}</c>
event which contains, in particular, the new
<seealso marker="#type-assoc_id">Association ID:</seealso></p>
@@ -233,6 +242,45 @@
</desc>
</func>
<func>
+ <name>connect_init(Socket, Addr, Port, Opts) -&gt; ok | {error, posix()}</name>
+ <fsummary>Same as <c>connect_init(Socket, Addr, Port, Opts, infinity)</c>.</fsummary>
+ <desc>
+ <p>Same as <c>connect_init(Socket, Addr, Port, Opts, infinity)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>connect_init(Socket, Addr, Port, [Opt], Timeout) -&gt; ok | {error, posix()}</name>
+ <fsummary>Initiate a new association for the socket <c>Socket</c>, with a peer (SCTP server socket)</fsummary>
+ <type>
+ <v>Socket = sctp_socket()</v>
+ <v>Addr = ip_address() | Host</v>
+ <v>Port = port_number()</v>
+ <v>Opt = sctp_option()</v>
+ <v>Timeout = timeout()</v>
+ <v>Host = atom() | string()</v>
+ </type>
+ <desc>
+ <p>Initiates a new association for the socket <c>Socket</c>,
+ with the peer (SCTP server socket) given by
+ <c>Addr</c> and <c>Port</c>.</p>
+ <p>The fundamental difference between this API
+ and <c>connect/*</c> is that the return value is that of the
+ underlying OS connect(2) system call. If <c>ok</c> is returned
+ then the result of the association establishement is received
+ by the calling process as
+ an <seealso marker="#record-sctp_assoc_change">
+ #sctp_assoc_change{}</seealso>
+ event. The calling process must be prepared to receive this, or
+ poll for it using <c>recv/*</c> depending on the value of the
+ active option.</p>
+ <p>The parameters are as described
+ in <seealso marker="#connect/5">connect/*</seealso>, with the
+ exception of the <c>Timeout</c> value.</p>
+ <p>The timer associated with <c>Timeout</c> only supervises
+ IP resolution of <c>Addr</c></p>
+ </desc>
+ </func>
+ <func>
<name>controlling_process(sctp_socket(), pid()) -&gt; ok</name>
<fsummary>Assign a new controlling process pid to the socket</fsummary>
<desc>
@@ -1058,6 +1106,65 @@
gen_sctp:close(S). </pre>
<p></p>
</item>
+ <item>
+ <p>A very simple Erlang SCTP Client which uses the
+ connect_init API.</p>
+ <pre>
+-module(ex3).
+
+-export([client/4]).
+-include_lib("kernel/include/inet.hrl").
+-include_lib("kernel/include/inet_sctp.hrl").
+
+client(Peer1, Port1, Peer2, Port2)
+ when is_tuple(Peer1), is_integer(Port1), is_tuple(Peer2), is_integer(Port2) -&gt;
+ {ok,S} = gen_sctp:open(),
+ SctpInitMsgOpt = {sctp_initmsg,#sctp_initmsg{num_ostreams=5}},
+ ActiveOpt = {active, true},
+ Opts = [SctpInitMsgOpt, ActiveOpt],
+ ok = gen_sctp:connect(S, Peer1, Port1, Opts),
+ ok = gen_sctp:connect(S, Peer2, Port2, Opts),
+ io:format("Connections initiated~n", []),
+ client_loop(S, Peer1, Port1, undefined, Peer2, Port2, undefined).
+
+client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, AssocId2) -&gt;
+ receive
+ {sctp, S, Peer1, Port1, {_Anc, SAC}}
+ when is_record(SAC, sctp_assoc_change), AssocId1 == undefined -&gt;
+ io:format("Association 1 connect result: ~p. AssocId: ~p~n",
+ [SAC#sctp_assoc_change.state,
+ SAC#sctp_assoc_change.assoc_id]),
+ client_loop(S, Peer1, Port1, SAC#sctp_assoc_change.assoc_id,
+ Peer2, Port2, AssocId2);
+
+ {sctp, S, Peer2, Port2, {_Anc, SAC}}
+ when is_record(SAC, sctp_assoc_change), AssocId2 == undefined -&gt;
+ io:format("Association 2 connect result: ~p. AssocId: ~p~n",
+ [SAC#sctp_assoc_change.state, SAC#sctp_assoc_change.assoc_id]),
+ client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2,
+ SAC#sctp_assoc_change.assoc_id);
+
+ {sctp, S, Peer1, Port1, Data} -&gt;
+ io:format("Association 1: received ~p~n", [Data]),
+ client_loop(S, Peer1, Port1, AssocId1,
+ Peer2, Port2, AssocId2);
+
+ {sctp, S, Peer2, Port2, Data} -&gt;
+ io:format("Association 2: received ~p~n", [Data]),
+ client_loop(S, Peer1, Port1, AssocId1,
+ Peer2, Port2, AssocId2);
+
+ Other -&gt;
+ io:format("Other ~p~n", [Other]),
+ client_loop(S, Peer1, Port1, AssocId1,
+ Peer2, Port2, AssocId2)
+
+ after 5000 -&gt;
+ ok
+ end.
+</pre>
+ <p></p>
+ </item>
</list>
</section>
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index fcd1d1564a..5a31e3976f 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -27,7 +27,7 @@
-include("inet_sctp.hrl").
-export([open/0,open/1,open/2,close/1]).
--export([listen/2,connect/4,connect/5]).
+-export([listen/2,connect/4,connect/5,connect_init/4,connect_init/5]).
-export([eof/2,abort/2]).
-export([send/3,send/4,recv/1,recv/2]).
-export([error_string/1]).
@@ -80,7 +80,26 @@ listen(S, Flag) ->
connect(S, Addr, Port, Opts) ->
connect(S, Addr, Port, Opts, infinity).
-connect(S, Addr, Port, Opts, Timeout) when is_port(S), is_list(Opts) ->
+connect(S, Addr, Port, Opts, Timeout) ->
+ case do_connect(S, Addr, Port, Opts, Timeout, true) of
+ badarg ->
+ erlang:error(badarg, [S,Addr,Port,Opts,Timeout]);
+ Result ->
+ Result
+ end.
+
+connect_init(S, Addr, Port, Opts) ->
+ connect_init(S, Addr, Port, Opts, infinity).
+
+connect_init(S, Addr, Port, Opts, Timeout) ->
+ case do_connect(S, Addr, Port, Opts, Timeout, false) of
+ badarg ->
+ erlang:error(badarg, [S,Addr,Port,Opts,Timeout]);
+ Result ->
+ Result
+ end.
+
+do_connect(S, Addr, Port, Opts, Timeout, ConnWait) when is_port(S), is_list(Opts) ->
case inet_db:lookup_socket(S) of
{ok,Mod} ->
case Mod:getserv(Port) of
@@ -89,21 +108,26 @@ connect(S, Addr, Port, Opts, Timeout) when is_port(S), is_list(Opts) ->
Timer ->
try Mod:getaddr(Addr, Timer) of
{ok,IP} ->
- Mod:connect(S, IP, Port, Opts, Timer);
+ ConnectTimer = if ConnWait == false ->
+ nowait;
+ true ->
+ Timer
+ end,
+ Mod:connect(S, IP, Port, Opts, ConnectTimer);
Error -> Error
after
inet:stop_timer(Timer)
end
catch
error:badarg ->
- erlang:error(badarg, [S,Addr,Port,Opts,Timeout])
+ badarg
end;
Error -> Error
end;
Error -> Error
end;
-connect(S, Addr, Port, Opts, Timeout) ->
- erlang:error(badarg, [S,Addr,Port,Opts,Timeout]).
+do_connect(_S, _Addr, _Port, _Opts, _Timeout, _ConnWait) ->
+ badarg.
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index 30c0e85dd9..795bf83807 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% SCTP protocol contribution by Leonid Timochouk and Serge Aleynikov.
@@ -64,16 +64,22 @@ close(S) ->
listen(S, Flag) ->
prim_inet:listen(S, Flag).
+%% A non-blocking connect is implemented when the initial call is to
+%% gen_sctp:connect_init which passes the value nowait as the Timer
connect(S, Addr, Port, Opts, Timer) ->
case prim_inet:chgopts(S, Opts) of
ok ->
case prim_inet:getopt(S, active) of
{ok,Active} ->
- Timeout = inet:timeout(Timer),
+ Timeout = if Timer =:= nowait ->
+ infinity; %% don't start driver timer in inet_drv
+ true ->
+ inet:timeout(Timer)
+ end,
case prim_inet:connect(S, Addr, Port, Timeout) of
- ok ->
+ ok when Timer =/= nowait ->
connect_get_assoc(S, Addr, Port, Active, Timer);
- Err1 -> Err1
+ OkOrErr1 -> OkOrErr1
end;
Err2 -> Err2
end;
@@ -89,10 +95,10 @@ connect(S, Addr, Port, Opts, Timer) ->
%% connect_get_assoc/5 below mistakes it for an invalid response
%% for a socket in {active,false} or {active,once} modes.
%%
-%% In {active,true} mode it probably gets right, but it is
-%% a blocking connect that is implemented even for {active,true},
-%% and that may be a shortcoming. A non-blocking connect
-%% would be nice to have.
+%% In {active,true} mode the window of time for the race is smaller,
+%% but it is possible and also it is a blocking connect that is
+%% implemented even for {active,true}, and that may be a
+%% shortcoming.
connect_get_assoc(S, Addr, Port, false, Timer) ->
case recv(S, inet:timeout(Timer)) of
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index dd7d5f111a..fad8c7398b 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_sctp_SUITE).
@@ -24,10 +24,11 @@
%%-compile(export_all).
-export([all/1,init_per_testcase/2,fin_per_testcase/2,
- basic/1,xfer_min/1,xfer_active/1,api_open_close/1,api_listen/1]).
+ basic/1,api_open_close/1,api_listen/1,api_connect_init/1,
+ xfer_min/1,xfer_active/1]).
all(suite) ->
- [basic,xfer_min,xfer_active,api_open_close,api_listen].
+ [basic,api_open_close,api_listen,api_connect_init,xfer_min,xfer_active].
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(15)),
@@ -325,7 +326,7 @@ api_listen(Config) when is_list(Config) ->
?line {ok,{Localhost,
Pb,[],
#sctp_assoc_change{
- state = comm_lost}}} =
+ state=comm_lost}}} =
gen_sctp:recv(Sa, infinity);
{error,#sctp_assoc_change{state=cant_assoc}} -> ok
end,
@@ -336,3 +337,48 @@ api_listen(Config) when is_list(Config) ->
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
ok.
+
+api_connect_init(doc) ->
+ "Test the API function connect_init/4";
+api_connect_init(suite) ->
+ [];
+api_connect_init(Config) when is_list(Config) ->
+ ?line Localhost = {127,0,0,1},
+
+ ?line {ok,S} = gen_sctp:open(),
+ ?line {ok,Pb} = inet:port(S),
+ ?line try gen_sctp:connect_init(S, Localhost, not_allowed_for_port, [])
+ catch error:badarg -> ok
+ end,
+ ?line try gen_sctp:connect_init(S, Localhost, 12345, not_allowed_for_opts)
+ catch error:badarg -> ok
+ end,
+ ?line ok = gen_sctp:close(S),
+ ?line {error,closed} = gen_sctp:connect_init(S, Localhost, 12345, []),
+
+ ?line {ok,Sb} = gen_sctp:open(Pb),
+ ?line {ok,Sa} = gen_sctp:open(),
+ ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
+ {error,econnrefused} ->
+ ?line {ok,{Localhost,
+ Pb,[],
+ #sctp_assoc_change{state=comm_lost}}} =
+ gen_sctp:recv(Sa, infinity);
+ ok ->
+ ?line {ok,{Localhost,
+ Pb,[],
+ #sctp_assoc_change{state=cant_assoc}}} =
+ gen_sctp:recv(Sa, infinity)
+ end,
+ ?line ok = gen_sctp:listen(Sb, true),
+ ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
+ ok ->
+ ?line {ok,{Localhost,
+ Pb,[],
+ #sctp_assoc_change{
+ state = comm_up}}} =
+ gen_sctp:recv(Sa, infinity)
+ end,
+ ?line ok = gen_sctp:close(Sa),
+ ?line ok = gen_sctp:close(Sb),
+ ok.