檢視原始碼 gen_sctp (核心 v10.2)
SCTP sockets 的介面。
此模組提供透過 SCTP sockets 通訊的功能。此實作假設作業系統核心透過使用者層級的 Sockets API 擴充支援 SCTP (RFC 2960)。
在開發期間,此實作已在以下環境測試:
- Linux Fedora Core 5.0 (需要核心 2.6.15-2054 或更新版本)
- Solaris 10, 11
在 OTP 適配期間,已在以下環境測試:
- SUSE Linux Enterprise Server 10 (x86_64) 核心 2.6.16.27-0.6-smp,搭配 lksctp-tools-1.0.6
- 在 Solaris 10 上簡要測試
- SUSE Linux Enterprise Server 10 Service Pack 1 (x86_64) 核心 2.6.16.54-0.2.3-smp,搭配 lksctp-tools-1.0.7
- FreeBSD 8.2
此模組是為一對多樣式的 sockets(類型為 seqpacket
)所撰寫。透過新增 peeloff/2
,引入了一對一樣式的 sockets(類型為 stream
)。
此模組的記錄定義可以使用以下方式找到:
-include_lib("kernel/include/inet_sctp.hrl").
這些記錄定義使用 "new" 的拼寫 'adaptation',而不是已棄用的 'adaption',無論底層 C API 使用哪種拼寫。
SCTP Socket 選項
可接受的 SCTP socket 選項集合在建構上與 TCP、UDP 和通用 inet
選項的集合正交。這裡僅列出允許用於 SCTP sockets 的選項。
選項可以在呼叫 open/1,2
時設定在 socket 上,並在呼叫 connect/4,5
或呼叫 inet:setopts/2
時變更。它們可以使用 inet:getopts/2
擷取。
{mode, list|binary} | list | binary
- 決定從recv/1,2
或在主動模式資料訊息中傳回的資料類型。如果
false
(被動模式,預設值),則呼叫端必須執行明確的recv
呼叫,以從 socket 擷取可用的資料。如果
true|once|N
(主動模式),接收到的資料或事件將會傳送至擁有程序。請參閱open/0..2
以瞭解訊息格式。如果
true
(完全主動模式),則沒有流量控制。注意
請注意,這可能會導致訊息佇列溢位,進而導致虛擬機器記憶體不足並崩潰。
如果
once
,則只有一則訊息會自動放置在訊息佇列中,且模式會重設為被動。這提供流量控制,並讓接收器能夠收聽與其他跨程序訊息交錯的傳入 SCTP 資料。如果將
active
指定為 -32768 至 32767 (含) 範圍內的整數N
,則該數字會加到 socket 的資料訊息計數器。如果相加的結果為負數,則計數器會設定為0
。一旦計數器達到0
,無論是透過傳送訊息或使用inet:setopts/2
明確設定,socket 模式都會重設為被動 ({active, false}
)。當處於{active, N}
模式的 socket 轉換為被動模式時,訊息{sctp_passive, Socket}
會傳送至控制程序,以通知它如果想要從 socket 接收更多資料訊息,則必須呼叫inet:setopts/2
將 socket 設定回主動模式。
{tos, integer()}
- 將傳送的 IP 資料包上的服務類型欄位設定為指定的值。這實際上決定了傳出封包的優先順序原則。可接受的值取決於系統。{priority, integer()}
- 與上述tos
等效的獨立於協定的選項。設定優先順序也會同時設定tos
。{dontroute, true|false}
- 預設值為false
。如果為true
,則核心不會透過任何閘道傳送封包,而只會將封包傳送至直接連線的主機。{reuseaddr, true|false}
- 預設值為false
。如果為true
,則可以立即重複使用 socket 的本機繫結位址{IP,Port}
。不會執行CLOSE_WAIT
狀態中的等待 (某些類型的伺服器可能需要)。{sndbuf, integer()}
- 此 socket 的 OS 核心傳送緩衝區的大小 (以位元組為單位)。如果資料包大於val(sndbuf)
,則會發生傳送錯誤。設定此選項也會調整驅動程式緩衝區的大小 (請參閱上方的buffer
)。{recbuf, integer()}
- 此 socket 的 OS 核心接收緩衝區的大小 (以位元組為單位)。如果資料包大於val(recbuf)
,則會發生傳送錯誤。設定此選項也會調整驅動程式緩衝區的大小 (請參閱上方的buffer
)。
{non_block_send, boolean()}
- 如果將此選項設定為true
,則原本會封鎖 (掛起) 的傳送呼叫,會立即傳回,例如{error, eagain}
。預設值為false
。{sctp_module, module()}
- 覆寫使用的回呼模組。IPv4 的預設值為inet_sctp
,IPv6 的預設值為inet6_sctp
。{sctp_rtoinfo, #sctp_rtoinfo{}}
#sctp_rtoinfo{ assoc_id = assoc_id(), initial = integer(), max = integer(), min = integer() }
決定由
assoc_id
指定的關聯的重傳逾時參數 (以毫秒為單位)。assoc_id = 0
(預設值) 表示整個端點。請參閱 RFC 2960 和 SCTP 的 Sockets API 擴充,以瞭解欄位值的確切語意。{sctp_associnfo, #sctp_assocparams{}}
#sctp_assocparams{ assoc_id = assoc_id(), asocmaxrxt = integer(), number_peer_destinations = integer(), peer_rwnd = integer(), local_rwnd = integer(), cookie_life = integer() }
決定由
assoc_id
指定的關聯的關聯參數。assoc_id = 0
(預設值) 表示整個端點。請參閱 SCTP 的 Sockets API 擴充,以瞭解其語意的討論。很少使用。{sctp_initmsg, #sctp_initmsg{}}
#sctp_initmsg{ num_ostreams = integer(), max_instreams = integer(), max_attempts = integer(), max_init_timeo = integer() }
決定此 socket 在與其對等節點建立關聯時嘗試協商的預設參數。應在
open/*
之後但在第一次connect/*
之前設定。#sctp_initmsg{}
也可以與第一次呼叫send/*
到新對等節點時 (建立新關聯時) 一起當作輔助資料使用。num_ostreams
- 外寄串流的數量max_instreams
- 入埠串流的最大數量max_attempts
- 建立關聯時的最大重傳次數max_init_timeo
- 建立關聯的逾時時間 (以毫秒為單位)
{sctp_autoclose, integer() >= 0}
- 決定閒置關聯自動關閉的時間 (以秒為單位)。0
表示永遠不會自動關閉關聯。{sctp_nodelay, true|false}
- 開啟|關閉 Nagle 演算法,以將小型封包合併為較大的封包。這可以提高輸送量,但會增加延遲。{sctp_disable_fragments, true|false}
- 如果為true
,則嘗試傳送大於目前 PMTU 大小的訊息時 (需要進行片段/重新組裝) 會產生錯誤。請注意,訊息片段不會影響其傳遞的邏輯原子性;提供此選項僅是為了效能考量。{sctp_i_want_mapped_v4_addr, true|false}
- 開啟|關閉自動將 IPv4 位址對應到 IPv6 位址 (如果 socket 位址系列為AF_INET6
)。{sctp_maxseg, integer()}
- 如果使用訊息片段,則決定最大區塊大小。如果為0
,則區塊大小僅受路徑 MTU 限制。{sctp_primary_addr, #sctp_prim{}}
#sctp_prim{ assoc_id = assoc_id(), addr = {IP, Port} } IP = ip_address() Port = port_number()
對於由
assoc_id
指定的關聯,{IP,Port}
必須是對等位址之一。此選項決定指定的位址將由本機 SCTP 堆疊視為對等節點的主要位址。{sctp_set_peer_primary_addr, #sctp_setpeerprim{}}
#sctp_setpeerprim{ assoc_id = assoc_id(), addr = {IP, Port} } IP = ip_address() Port = port_number()
設定後,通知對等節點使用
{IP, Port}
作為由assoc_id
指定的關聯的本機端點的主要位址。{sctp_adaptation_layer, #sctp_setadaptation{}}
#sctp_setadaptation{ adaptation_ind = integer() }
設定後,會要求本機端點使用
adaptation_ind
指定的值,作為建立新關聯的「適配指示」參數。如需詳細資訊,請參閱 RFC 2960 和 SCTP 的 Sockets API 擴充。{sctp_peer_addr_params, #sctp_paddrparams{}}
#sctp_paddrparams{ assoc_id = assoc_id(), address = {IP, Port}, hbinterval = integer(), pathmaxrxt = integer(), pathmtu = integer(), sackdelay = integer(), flags = list() } IP = ip_address() Port = port_number()
決定由
assoc_id
指定的關聯和對等位址address
的各種每個位址參數 (SCTP 協定支援多重歸屬,因此多個位址可以對應到指定的關聯)。hbinterval
- 心跳間隔 (以毫秒為單位)pathmaxrxt
- 在將此位址視為無法連線 (並選取替代位址) 之前的最大重傳次數pathmtu
- 固定路徑 MTU,如果停用自動探索(請參閱下方的flags
)sackdelay
- SAC 訊息的延遲時間,以毫秒為單位(如果啟用延遲,請參閱下方的flags
)flags
- 以下旗標可用hb_enable
- 啟用心跳訊號hb_disable
- 停用心跳訊號hb_demand
- 立即啟動心跳訊號pmtud_enable
- 啟用自動路徑 MTU 探索pmtud_disable
- 停用自動路徑 MTU 探索sackdelay_enable
- 啟用 SAC 延遲sackdelay_disable
- 停用 SAC 延遲
{sctp_default_send_param, #sctp_sndrcvinfo{}}
#sctp_sndrcvinfo{ stream = integer(), ssn = integer(), flags = list(), ppid = integer(), context = integer(), timetolive = integer(), tsn = integer(), cumtsn = integer(), assoc_id = assoc_id() }
#sctp_sndrcvinfo{}
用於此 socket 選項,以及傳送或接收 SCTP 訊息時的輔助資料。當設定為選項時,它會為後續在由assoc_id
指定的關聯上呼叫send
提供預設值。assoc_id = 0
(預設值)表示整個端點。以下欄位通常必須由傳送者指定
sinfo_stream
- 要透過關聯傳送訊息的串流編號(以 0 為起始);sinfo_flags
- 以下旗標可識別unordered
- 訊息將以無序方式傳送addr_over
- 在send
中指定的位址會覆寫主要對等位址abort
- 中止目前的關聯,而不刷新任何未傳送的資料eof
- 優雅地關閉目前的關聯,並刷新未傳送的資料
其他欄位很少使用。如需完整資訊,請參閱 RFC 2960 和 SCTP 的 Socket API 擴充功能。
{sctp_events, #sctp_event_subscribe{}}
#sctp_event_subscribe{ data_io_event = true | false, association_event = true | false, address_event = true | false, send_failure_event = true | false, peer_error_event = true | false, shutdown_event = true | false, partial_delivery_event = true | false, adaptation_layer_event = true | false }
此選項決定要接收哪些 SCTP 事件(透過
recv/*
)以及資料。唯一的例外是data_io_event
,它會啟用或停用接收#sctp_sndrcvinfo{}
輔助資料,而不是事件。依預設,除了adaptation_layer_event
之外的所有旗標都已啟用,儘管sctp_data_io_event
和association_event
由驅動程式本身使用,且不會匯出到使用者層級。{sctp_delayed_ack_time, #sctp_assoc_value{}}
#sctp_assoc_value{ assoc_id = assoc_id(), assoc_value = integer() }
很少使用。決定指定關聯的 ACK 時間(由
assoc_value
指定,以毫秒為單位),如果assoc_value = 0
(預設值),則決定整個端點的 ACK 時間。{sctp_status, #sctp_status{}}
#sctp_status{ assoc_id = assoc_id(), state = atom(), rwnd = integer(), unackdata = integer(), penddata = integer(), instrms = integer(), outstrms = integer(), fragmentation_point = integer(), primary = #sctp_paddrinfo{} }
此選項為唯讀。它決定由
assoc_id
指定的 SCTP 關聯狀態。以下是state
的可能值(狀態名稱大部分都容易理解)sctp_state_empty
- 預設值。表示沒有其他狀態處於活動狀態。sctp_state_closed
sctp_state_cookie_wait
sctp_state_cookie_echoed
sctp_state_established
sctp_state_shutdown_pending
sctp_state_shutdown_sent
sctp_state_shutdown_received
sctp_state_shutdown_ack_sent
其他欄位的語意
sstat_rwnd
- 關聯的目前接收視窗大小sstat_unackdata
- 未確認資料區塊的數量sstat_penddata
- 等待接收的資料區塊數量sstat_instrms
- 輸入串流的數量sstat_outstrms
- 輸出串流的數量sstat_fragmentation_point
- 發生 SCTP 分割的訊息大小sstat_primary
- 目前主要對等位址的資訊(#sctp_paddrinfo{}
的格式請參閱下方)
{sctp_get_peer_addr_info, #sctp_paddrinfo{}}
#sctp_paddrinfo{ assoc_id = assoc_id(), address = {IP, Port}, state = inactive | active | unconfirmed, cwnd = integer(), srtt = integer(), rto = integer(), mtu = integer() } IP = ip_address() Port = port_number()
此選項為唯讀。它決定由
address
指定的對等位址,在由assoc_id
指定的關聯中之特定參數。呼叫者必須設定欄位address
;所有其他欄位都會在傳回時填入。如果assoc_id = 0
(預設值),address
會自動轉換為對應的關聯 ID。此選項很少使用。如需所有欄位的語意,請參閱 RFC 2960 和 SCTP 的 Socket API 擴充功能。
SCTP 範例
接收 SCTP 訊息並將其列印在標準輸出的 Erlang SCTP 伺服器範例
-module(sctp_server).
-export([server/0,server/1,server/2]).
-include_lib("kernel/include/inet.hrl").
-include_lib("kernel/include/inet_sctp.hrl").
server() ->
server(any, 2006).
server([Host,Port]) when is_list(Host), is_list(Port) ->
{ok, #hostent{h_addr_list = [IP|_]}} = inet:gethostbyname(Host),
io:format("~w -> ~w~n", [Host, IP]),
server([IP, list_to_integer(Port)]).
server(IP, Port) when is_tuple(IP) orelse IP == any orelse IP == loopback,
is_integer(Port) ->
{ok,S} = gen_sctp:open(Port, [{recbuf,65536}, {ip,IP}]),
io:format("Listening on ~w:~w. ~w~n", [IP,Port,S]),
ok = gen_sctp:listen(S, true),
server_loop(S).
server_loop(S) ->
case gen_sctp:recv(S) of
{error, Error} ->
io:format("SCTP RECV ERROR: ~p~n", [Error]);
Data ->
io:format("Received: ~p~n", [Data])
end,
server_loop(S).
與上述伺服器互動的 Erlang SCTP 用戶端範例。請注意,在此範例中,用戶端建立與伺服器具有 5 個輸出串流的關聯。因此,透過串流 0 傳送 "Test 0"
成功,但透過串流 5 傳送 "Test 5"
失敗。然後,用戶端 abort
關聯,這導致伺服器端接收到對應的事件。
-module(sctp_client).
-export([client/0, client/1, client/2]).
-include_lib("kernel/include/inet.hrl").
-include_lib("kernel/include/inet_sctp.hrl").
client() ->
client([localhost]).
client([Host]) ->
client(Host, 2006);
client([Host, Port]) when is_list(Host), is_list(Port) ->
client(Host,list_to_integer(Port)),
init:stop().
client(Host, Port) when is_integer(Port) ->
{ok,S} = gen_sctp:open(),
{ok,Assoc} = gen_sctp:connect
(S, Host, Port, [{sctp_initmsg,#sctp_initmsg{num_ostreams=5}}]),
io:format("Connection Successful, Assoc=~p~n", [Assoc]),
io:write(gen_sctp:send(S, Assoc, 0, <<"Test 0">>)),
io:nl(),
timer:sleep(10000),
io:write(gen_sctp:send(S, Assoc, 5, <<"Test 5">>)),
io:nl(),
timer:sleep(10000),
io:write(gen_sctp:abort(S, Assoc)),
io:nl(),
timer:sleep(1000),
gen_sctp:close(S).
使用 connect_init
API 的簡單 Erlang SCTP 用戶端
-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) ->
{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) ->
receive
{sctp, S, Peer1, Port1, {_Anc, SAC}}
when is_record(SAC, sctp_assoc_change), AssocId1 == undefined ->
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 ->
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} ->
io:format("Association 1: received ~p~n", [Data]),
client_loop(S, Peer1, Port1, AssocId1,
Peer2, Port2, AssocId2);
{sctp, S, Peer2, Port2, Data} ->
io:format("Association 2: received ~p~n", [Data]),
client_loop(S, Peer1, Port1, AssocId1,
Peer2, Port2, AssocId2);
Other ->
io:format("Other ~p~n", [Other]),
client_loop(S, Peer1, Port1, AssocId1,
Peer2, Port2, AssocId2)
after 5000 ->
ok
end.
另請參閱
gen_tcp
、gen_udp
、inet
、RFC 2960(串流控制傳輸協定)、SCTP 的 Socket API 擴充功能
摘要
類型:匯出的資料類型
關聯 ID。
SCTP Socket 選項名稱和值,用於設定。
SCTP Socket 選項名稱,用於取得。
SCTP Socket 選項名稱和值,您所取得的內容。
從 open/*
傳回的 Socket 識別碼。
函式
中止關聯。
關閉 SCTP socket。
與對等端建立關聯。
開始與對等端建立關聯。
開始與對等端建立關聯。
開始與對等端建立關聯(多個位址)。
開始與對等端建立關聯(多個位址)。
變更 socket 的控制程序(擁有者)。
優雅地終止關聯。
將錯誤編號轉換為字串或 atom。
使 SCTP socket 監聽連入關聯。
建立 SCTP socket。
將關聯分支為 stream
類型的新 socket。
接收 Data
訊息。
傳送 Data
訊息,功能完整。
類型:匯出的資料類型
-type assoc_id() :: term().
關聯 ID。
例如,在 #sctp_paddr_change{}
中傳回的不透明詞彙,它會識別 SCTP socket 的關聯。除了特殊值 0
之外,此詞彙是不透明的,該值具有「整個端點」或「所有未來關聯」等含意。
-type option() :: elementary_option() | record_option().
SCTP Socket 選項名稱和值,用於設定。
-type option_name() :: elementary_option_name() | record_option() | ro_option().
SCTP Socket 選項名稱,用於取得。
-type option_value() :: elementary_option() | record_option() | ro_option().
SCTP Socket 選項名稱和值,您所取得的內容。
-type sctp_socket() :: port().
從 open/*
傳回的 Socket 識別碼。
類型:內部資料類型
-type elementary_option() :: {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {non_block_send, boolean()} | {debug, boolean()} | {dontroute, boolean()} | {exclusiveaddruse, boolean()} | {high_msgq_watermark, pos_integer()} | {linger, {boolean(), non_neg_integer()}} | {low_msgq_watermark, pos_integer()} | {mode, list | binary} | list | binary | {priority, non_neg_integer()} | {recbuf, non_neg_integer()} | {reuseaddr, boolean()} | {reuseport, boolean()} | {reuseport_lb, boolean()} | {ipv6_v6only, boolean()} | {sndbuf, non_neg_integer()} | {sctp_autoclose, non_neg_integer()} | {sctp_disable_fragments, boolean()} | {sctp_i_want_mapped_v4_addr, boolean()} | {sctp_maxseg, non_neg_integer()} | {sctp_nodelay, boolean()} | {tos, non_neg_integer()} | {tclass, non_neg_integer()} | {ttl, non_neg_integer()} | {recvtos, boolean()} | {recvtclass, boolean()} | {recvttl, boolean()}.
-type elementary_option_name() ::
active | buffer | non_block_send | debug | dontroute | exclusiveaddruse |
high_msgq_watermark | linger | low_msgq_watermark | mode | priority | recbuf | reuseaddr |
reuseport | reuseport_lb | ipv6_v6only | sctp_autoclose | sctp_disable_fragments |
sctp_i_want_mapped_v4_addr | sctp_maxseg | sctp_nodelay | sndbuf | tos | tclass | ttl |
recvtos | recvtclass | recvttl.
-type record_option() :: {sctp_adaptation_layer, #sctp_setadaptation{adaptation_ind :: term()}} | {sctp_associnfo, #sctp_assocparams{assoc_id :: term(), asocmaxrxt :: term(), number_peer_destinations :: term(), peer_rwnd :: term(), local_rwnd :: term(), cookie_life :: term()}} | {sctp_default_send_param, #sctp_sndrcvinfo{stream :: term(), ssn :: term(), flags :: term(), ppid :: term(), context :: term(), timetolive :: term(), tsn :: term(), cumtsn :: term(), assoc_id :: term()}} | {sctp_delayed_ack_time, #sctp_assoc_value{assoc_id :: term(), assoc_value :: term()}} | {sctp_events, #sctp_event_subscribe{data_io_event :: term(), association_event :: term(), address_event :: term(), send_failure_event :: term(), peer_error_event :: term(), shutdown_event :: term(), partial_delivery_event :: term(), adaptation_layer_event :: term(), authentication_event :: term()}} | {sctp_initmsg, #sctp_initmsg{num_ostreams :: term(), max_instreams :: term(), max_attempts :: term(), max_init_timeo :: term()}} | {sctp_peer_addr_params, #sctp_paddrparams{assoc_id :: term(), address :: term(), hbinterval :: term(), pathmaxrxt :: term(), pathmtu :: term(), sackdelay :: term(), flags :: term()}} | {sctp_primary_addr, #sctp_prim{assoc_id :: term(), addr :: term()}} | {sctp_rtoinfo, #sctp_rtoinfo{assoc_id :: term(), initial :: term(), max :: term(), min :: term()}} | {sctp_set_peer_primary_addr, #sctp_setpeerprim{assoc_id :: term(), addr :: term()}}.
-type ro_option() :: {sctp_get_peer_addr_info, #sctp_paddrinfo{assoc_id :: term(), address :: term(), state :: term(), cwnd :: term(), srtt :: term(), rto :: term(), mtu :: term()}} | {sctp_status, #sctp_status{assoc_id :: term(), state :: term(), rwnd :: term(), unackdata :: term(), penddata :: term(), instrms :: term(), outstrms :: term(), fragmentation_point :: term(), primary :: term()}}.
函式
-spec abort(Socket, Assoc) -> ok | {error, inet:posix()} when Socket :: sctp_socket(), Assoc :: #sctp_assoc_change{state :: term(), error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}.
中止關聯。
異常終止由 Assoc
指定的關聯,而不刷新未傳送的資料。socket 本身保持開啟。在此 socket 上開啟的其他關聯仍然有效,而且 socket 可用於新的關聯。
-spec close(Socket) -> ok | {error, inet:posix()} when Socket :: sctp_socket().
關閉 SCTP socket。
關閉 socket 及其上的所有關聯。未傳送的資料會像 eof/2
一樣刷新。close/1
呼叫會根據 linger
socket [選項] 的值而變成封鎖狀態。如果它是 false
或延遲逾時到期,則呼叫會傳回,而且未傳送的資料會在背景中刷新。
-spec connect(Socket, SockAddr, Opts) -> {ok, #sctp_assoc_change{state :: comm_up, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, #sctp_assoc_change{state :: cant_assoc, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, inet:posix()} when Socket :: sctp_socket(), SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Opts :: [Opt :: option()].
-spec connect(Socket, SockAddr, Opts, Timeout) -> {ok, #sctp_assoc_change{state :: comm_up, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, #sctp_assoc_change{state :: cant_assoc, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, inet:posix()} when Socket :: sctp_socket(), SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Opts :: [Opt :: option()], Timeout :: timeout(); (Socket, Addr, Port, Opts) -> {ok, #sctp_assoc_change{state :: comm_up, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, #sctp_assoc_change{state :: cant_assoc, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, inet:posix()} when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [Opt :: option()].
與對等端建立關聯。
使用引數 Addr
和 Port
時,等同於 connect(Socket, Addr, Port, Opts, infinity)
。
使用引數 SockAddr
和 Opts
時 (自 OTP 24.3 起),等同於 connect(Socket, Addr, Port, Opts, Timeout)
,其中 Addr
和 Port
是從 SockAddr
擷取。
-spec connect(Socket, Addr, Port, Opts, Timeout) -> {ok, #sctp_assoc_change{state :: comm_up, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, #sctp_assoc_change{state :: cant_assoc, error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}} | {error, inet:posix()} when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [Opt :: option()], Timeout :: timeout().
與對等端建立關聯。
為 socket Socket
建立新的關聯,其對等端(SCTP 伺服器 socket)由 Addr
和 Port
指定。Timeout
以毫秒表示。一個 socket 可以與多個對等端關聯。socket 的類型必須為 seqpacket
。
警告
如果
Timeout
的值小於作業系統建立關聯所需的最大時間(如果使用 RFC 4960 的預設值,則約為 4.5 分鐘),可能會導致回傳值不一致或不正確。對於共享相同Socket
(即來源位址和連接埠)的關聯而言,這一點尤其重要,因為控制程序會阻塞直到connect/*
回傳。connect_init/*
提供了一種沒有此限制的替代方案。
#sctp_assoc_change{}
connect/*
的結果是一個 #sctp_assoc_change{}
事件,其中特別包含新的 關聯 ID:l
#sctp_assoc_change{
state = atom(),
error = integer(),
outbound_streams = integer(),
inbound_streams = integer(),
assoc_id = assoc_id()
}
可以透過將 sctp_initmsg
選項給予 connect
來設定關聯的輸出和輸入串流數量,如下所示
connect(Socket, Ip, Port>,
[{sctp_initmsg,#sctp_initmsg{num_ostreams=OutStreams,
max_instreams=MaxInStreams}}])
在嘗試關聯之前,會先在 socket 上設定所有選項 Opt
。如果選項記錄具有未定義的欄位值,則會先從 socket 讀取這些值的選項記錄。實際上,Opt
選項記錄只需要定義連接之前要變更的欄位值。
回傳的 outbound_streams
和 inbound_streams
是 socket 上的串流數量。如果對等端需要較低的值,這些值可能與請求的值(分別為 OutStreams
和 MaxInStreams
)不同。
state
可以具有以下值
comm_up
- 關聯已成功建立。這表示connect
已成功完成。cant_assoc
- 無法建立關聯(connect/*
失敗)。
其他狀態通常不會出現在 connect/*
的輸出中。相反地,它們可能會出現在接收到的 #sctp_assoc_change{}
事件中,而不是來自 recv/*
呼叫或 socket 訊息的資料。所有這些狀態都表示由於各種錯誤情況而失去關聯,並在此處列出以求完整性
comm_lost
restart
shutdown_comp
欄位 error
可以提供更詳細的診斷資訊。可以使用 error_string/1
將其值轉換為字串。
-spec connect_init(Socket, SockAddr, Opts) -> ok | {error, inet:posix()} when Socket :: sctp_socket(), SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Opts :: [option()].
-spec connect_init(Socket, SockAddr, Opts, Timeout) -> ok | {error, inet:posix()} when Socket :: sctp_socket(), SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Opts :: [option()], Timeout :: timeout(); (Socket, Addr, Port, Opts) -> ok | {error, inet:posix()} when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [option()].
開始與對等端建立關聯。
使用引數 Addr
和 Port
,等同於 connect_init(Socket, Addr, Port, Opts, infinity)
。
使用引數 SockAddr
和 Opts
(自 OTP 24.3 起),等同於 connect_init(Socket, Addr, Port, Opts, Timeout)
,其中 Addr
和 Port
是從 SockAddr
擷取的。
-spec connect_init(Socket, Addr, Port, Opts, Timeout) -> ok | {error, inet:posix()} when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [option()], Timeout :: timeout().
開始與對等端建立關聯。
為 socket Socket
啟動新的關聯,其對等端(SCTP 伺服器 socket)由 Addr
和 Port
指定。
此 API 與 connect/*
的根本差異在於,回傳值是底層作業系統 connect(2)
系統呼叫的回傳值。如果回傳 ok
,則表示操作已成功啟動,並且關聯建立的最終結果將以 #sctp_assoc_change{}
事件的形式傳送到 socket 所有者(控制程序)。socket 所有者必須準備好接收此事件,並且必須輪詢 recv/*
呼叫,具體取決於 active 選項的值。
參數的說明與 connect/*
的說明相同,但 Timeout
值除外,因為此函數的逾時僅適用於 Addr
為 inet:hostname/0
時的名稱解析。
-spec connectx_init(Socket, SockAddrs, Opts) -> {ok, assoc_id()} | {error, inet:posix()} when Socket :: sctp_socket(), SockAddrs :: [{inet:ip_address(), inet:port_number()} | inet:family_address() | socket:sockaddr_in() | socket:sockaddr_in6()], Opts :: [option()].
開始與對等端建立關聯(多個位址)。
與 connectx_init/5
類似,但使用 socket 位址,並且沒有 Timeout
。由於位址不需要查找,且連線是非阻塞的,因此此呼叫會立即回傳。
每個 socket 位址的 port
值必須相同或為零。至少一個 socket 位址必須具有非零的 port
。
-spec connectx_init(Socket, Addrs, Port, Opts) -> {ok, assoc_id()} | {error, inet:posix()} when Socket :: sctp_socket(), Addrs :: [inet:ip_address() | inet:hostname()], Port :: inet:port_number() | atom(), Opts :: [option()].
-spec connectx_init(Socket, Addrs, Port, Opts, Timeout) -> {ok, assoc_id()} | {error, inet:posix()} when Socket :: sctp_socket(), Addrs :: [inet:ip_address() | inet:hostname()], Port :: inet:port_number() | atom(), Opts :: [option()], Timeout :: timeout().
開始與對等端建立關聯(多個位址)。
為 socket Socket
啟動新的關聯,其對等端(SCTP 伺服器 socket)由 Addrs
和 Port
指定。
此 API 與 connect_init/*
類似,但使用底層作業系統的 sctp_connectx(3)
系統呼叫,該呼叫接受多個目標位址。
如果成功,則會回傳關聯 ID,該 ID 將在後續的 #sctp_assoc_change{}
事件中接收。
參數的說明如 connect_init/5
中所述
注意
此 API 允許作業系統在建立關聯時使用所有 Addrs,但不保證一定會使用。因此,如果連線失敗,使用者可能需要輪換位址順序以進行後續呼叫。
-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: sctp_socket(), Pid :: pid(), Reason :: closed | not_owner | badarg | inet:posix().
變更 socket 的控制程序(擁有者)。
將新的控制程序 Pid
指派給 Socket
。請參閱 gen_udp:controlling_process/2
。
-spec eof(Socket, Assoc) -> ok | {error, Reason} when Socket :: sctp_socket(), Assoc :: #sctp_assoc_change{state :: term(), error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()}, Reason :: term().
優雅地終止關聯。
正常終止由 Assoc
指定的關聯,並刷新所有未傳送的資料。socket 本身保持開啟。在此 socket 上開啟的其他關聯仍然有效。socket 可用於新的關聯。
將錯誤編號轉換為字串或 atom。
將 SCTP 錯誤編號(例如,來自 #sctp_remote_error{}
或 #sctp_send_failed{}
)轉換為說明性字串,或轉換為原子 ok
(表示沒有錯誤)或 unknown_error
(表示無法識別的整數)。
-spec listen(Socket, IsServer) -> ok | {error, Reason} when Socket :: sctp_socket(), IsServer :: boolean(), Reason :: term(); (Socket, Backlog) -> ok | {error, Reason} when Socket :: sctp_socket(), Backlog :: integer(), Reason :: term().
使 SCTP socket 監聽連入關聯。
socket 將會監聽它所繫結的 IP 位址和連接埠號碼。
對於 seqpacket
類型的 socket(預設值),引數 IsServer
必須是 boolean/0
。與 stream
socket 不同,沒有監聽佇列長度。如果 IsServer
為 true
,則 socket 會接受新的關聯,也就是說,它會成為 SCTP 伺服器 socket。
對於 stream
類型的 socket,引數 Backlog
設定後端佇列長度,就像 TCP 一樣。
-spec open() -> {ok, Socket} | {error, inet:posix()} when Socket :: sctp_socket().
等同於 open([])
。
-spec open(Port) -> {ok, Socket} | {error, inet:posix()} when Port :: inet:port_number(), Socket :: sctp_socket(); (Opts) -> {ok, Socket} | {error, inet:posix()} when Opts :: [Opt], Opt :: {ifaddr, IP | SockAddr} | {ip, IP} | {port, Port} | inet:address_family() | {type, SockType} | {netns, file:filename_all()} | {bind_to_device, binary()} | option(), IP :: inet:ip_address() | any | loopback, SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Port :: inet:port_number(), SockType :: seqpacket | stream, Socket :: sctp_socket().
建立 SCTP socket。
使用引數 Port
,等同於 open([{port, Port}]
。
建立一個 SCTP socket,並將其繫結到所有 {ip,IP}
(或同義詞 {ifaddr,IP}
)選項指定的本機位址(此功能稱為 SCTP 多重歸屬)。預設的 IP
和 Port
為 any
和 0
,表示繫結到任何可用連接埠上的所有本機位址。
也可以使用 {ifaddr, SockAddr}
,在這種情況下,它會優先於 ip
和 port
選項。但是,這些選項可用於更新 ifaddr 的位址和連接埠(如果它們在選項列表中出現在 ifaddr 之後),儘管不建議這樣做。
其他選項
inet6
- 為 IPv6 設定 socket。inet
- 為 IPv4 設定 socket。這是預設值。
使用一組預設的 socket 選項。特別是,socket 在 binary 和 passive 模式下開啟,SockType 為 seqpacket
,並具有相當大的 核心和驅動程式 緩衝區。
當 socket 處於 passive 模式時,可以透過 recv/1,2
呼叫接收資料。
當 socket 處於 active 模式時,接收到的資料會以訊息的形式傳遞到控制程序
{sctp, Socket, FromIP, FromPort, {AncData, Data}}
有關訊息欄位的說明,請參閱 recv/1,2
。
注意
遺憾的是,此訊息格式與包含輔助資料的
gen_udp
訊息格式以及recv/1,2
回傳的 tuple 格式略有不同。
-spec peeloff(Socket, Assoc) -> {ok, NewSocket} | {error, Reason} when Socket :: sctp_socket(), Assoc :: #sctp_assoc_change{state :: term(), error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()} | assoc_id(), NewSocket :: sctp_socket(), Reason :: term().
將關聯分支為 stream
類型的新 socket。
socket Socket
中現有的關聯 Assoc
(必須是 seqpacket
類型;一對多樣式)被分支到一個新的 stream
類型 socket NewSocket
(一對一樣式)。
現有關聯引數 Assoc
可以是 #sctp_assoc_change{}
記錄,如 recv/*
、connect/*
回傳的記錄,或是來自 active 模式下監聽 socket 的記錄。它也可以只是來自該記錄的 assoc_id
欄位 integer/0
。
-spec recv(Socket) -> {ok, {FromIP, FromPort, AncData, Data}} | {error, Reason} when Socket :: sctp_socket(), FromIP :: inet:ip_address(), FromPort :: inet:port_number(), AncData :: [#sctp_sndrcvinfo{stream :: term(), ssn :: term(), flags :: term(), ppid :: term(), context :: term(), timetolive :: term(), tsn :: term(), cumtsn :: term(), assoc_id :: term()} | inet:ancillary_data()], Data :: binary() | string() | #sctp_sndrcvinfo{stream :: term(), ssn :: term(), flags :: term(), ppid :: term(), context :: term(), timetolive :: term(), tsn :: term(), cumtsn :: term(), assoc_id :: term()} | #sctp_assoc_change{state :: term(), error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()} | #sctp_paddr_change{addr :: term(), state :: term(), error :: term(), assoc_id :: term()} | #sctp_adaptation_event{adaptation_ind :: term(), assoc_id :: term()}, Reason :: inet:posix() | #sctp_send_failed{flags :: term(), error :: term(), info :: term(), assoc_id :: term(), data :: term()} | #sctp_paddr_change{addr :: term(), state :: term(), error :: term(), assoc_id :: term()} | #sctp_pdapi_event{indication :: term(), assoc_id :: term()} | #sctp_remote_error{error :: term(), assoc_id :: term(), data :: term()} | #sctp_shutdown_event{assoc_id :: term()}.
-spec recv(Socket, Timeout) -> {ok, {FromIP, FromPort, AncData, Data}} | {error, Reason} when Socket :: sctp_socket(), Timeout :: timeout(), FromIP :: inet:ip_address(), FromPort :: inet:port_number(), AncData :: [#sctp_sndrcvinfo{stream :: term(), ssn :: term(), flags :: term(), ppid :: term(), context :: term(), timetolive :: term(), tsn :: term(), cumtsn :: term(), assoc_id :: term()} | inet:ancillary_data()], Data :: binary() | string() | #sctp_sndrcvinfo{stream :: term(), ssn :: term(), flags :: term(), ppid :: term(), context :: term(), timetolive :: term(), tsn :: term(), cumtsn :: term(), assoc_id :: term()} | #sctp_assoc_change{state :: term(), error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()} | #sctp_paddr_change{addr :: term(), state :: term(), error :: term(), assoc_id :: term()} | #sctp_adaptation_event{adaptation_ind :: term(), assoc_id :: term()}, Reason :: inet:posix() | #sctp_send_failed{flags :: term(), error :: term(), info :: term(), assoc_id :: term(), data :: term()} | #sctp_paddr_change{addr :: term(), state :: term(), error :: term(), assoc_id :: term()} | #sctp_pdapi_event{indication :: term(), assoc_id :: term()} | #sctp_remote_error{error :: term(), assoc_id :: term(), data :: term()} | #sctp_shutdown_event{assoc_id :: term()}.
接收 Data
訊息。
從 socket 的任何關聯接收 Data
訊息。如果接收逾時,則會回傳 {error,timeout}
。預設逾時為 infinity
。FromIP
和 FromPort
表示傳送端的位址。
AncData
是與主要 Data
一起接收的輔助資料項目清單。此清單可以為空,或者如果啟用接收輔助資料(請參閱選項 sctp_events
),則包含單一 #sctp_sndrcvinfo{}
記錄。預設情況下,它是啟用的,因為此類輔助資料提供了一種簡單的方法來確定接收訊息的關聯和串流。(另一種方法是使用 socket 選項 sctp_get_peer_addr_info
從 FromIP
和 FromPort
取得關聯 ID,但這仍然無法提供串流編號)。
AncData
也可能包含來自 socket 選項 recvtos
、recvtclass
或 recvttl
的 輔助資料,如果平台上的 socket 支援此功能的話。
接收到的 Data
,根據 socket 模式 的不同,可以是 binary/0
或位元組(範圍在 0
到 255
的整數)的 list/0
,或者它可以是一個 SCTP 事件。
可能的 SCTP 事件
#sctp_paddr_change{ addr = {ip_address(),port()}, state = atom(), error = integer(), assoc_id = assoc_id() }
表示由
assoc_id
關聯中指定的對等方的 IP 位址狀態變更。state
的可能值(大多不言自明)包括addr_unreachable
addr_available
addr_removed
addr_added
addr_made_prim
addr_confirmed
如果發生錯誤(例如,
addr_unreachable
),欄位error
提供更多診斷資訊。在這種情況下,事件#sctp_paddr_change{}
會自動轉換為recv
返回的error
項。可以使用error_string/1
將error
欄位值轉換為字串。#sctp_send_failed{ flags = true | false, error = integer(), info = #sctp_sndrcvinfo{}, assoc_id = assoc_id() data = binary() }
如果傳送操作失敗,傳送者可以接收此事件。
flags
- 一個布林值,指定資料是否已透過網路傳輸。error
- 提供擴充的診斷資訊,請使用error_string/1
.info
- 在失敗的send/*
中使用的原始#sctp_sndrcvinfo{}
記錄。data
- 嘗試傳送的整個原始資料區塊。
在 Erlang/SCTP 綁定的目前實作中,此事件會在內部轉換為
recv/*
返回的error
項。#sctp_adaptation_event{ adaptation_ind = integer(), assoc_id = assoc_id() }
當對等方傳送適應層指示參數(透過選項
sctp_adaptation_layer
配置)時傳遞。請注意,在 Erlang/SCTP 綁定的目前實作中,此事件預設為停用。#sctp_pdapi_event{ indication = sctp_partial_delivery_aborted, assoc_id = assoc_id() }
部分傳遞失敗。在 Erlang/SCTP 綁定的目前實作中,此事件會在內部轉換為
recv/*
返回的error
項。
-spec send(Socket, SndRcvInfo, Data) -> ok | {error, Reason} when Socket :: sctp_socket(), SndRcvInfo :: #sctp_sndrcvinfo{stream :: term(), ssn :: term(), flags :: term(), ppid :: term(), context :: term(), timetolive :: term(), tsn :: term(), cumtsn :: term(), assoc_id :: term()}, Data :: binary() | iolist(), Reason :: term().
傳送 Data
訊息,功能完整。
使用 #sctp_sndrcvinfo{}
記錄中的所有傳送參數傳送 Data
訊息。這樣,使用者可以指定 PPID(傳遞到遠端)和上下文(傳遞到本機 SCTP 層),例如,可用於錯誤識別。但是,很少需要如此精細的使用者控制。函式 send/4
對於大多數應用程式來說已足夠。
注意
傳送通常是阻塞的,但是如果 socket 選項
non_block_send
設定為 true,則當函式在其他情況下會阻塞時,該函式將返回例如 {error, eagain}。然後由使用者稍後再次嘗試。
-spec send(Socket, Assoc, Stream, Data) -> ok | {error, Reason} when Socket :: sctp_socket(), Assoc :: #sctp_assoc_change{state :: term(), error :: term(), outbound_streams :: term(), inbound_streams :: term(), assoc_id :: term()} | assoc_id(), Stream :: integer(), Data :: binary() | iolist(), Reason :: term().
傳送資料訊息。
在關聯 Assoc
和 Stream
上傳送 Data
訊息。
Assoc
可以使用來自關聯建立的 #sctp_assoc_change{}
記錄指定,或者作為 assoc_id/0
integer/0
欄位值指定。
注意
傳送通常是阻塞的,但是如果 socket 選項
non_block_send
設定為 true,則當函式在其他情況下會阻塞時,該函式將返回例如 {error, eagain}。然後由使用者稍後再次嘗試。