gen_sctp
gen_sctp
模块
gen_sctp
模块摘要
使用 SCTP 协议与套接字进行通信的函数。
描述
此模块提供了使用 SCTP 协议与套接字进行通信的功能。实现假定 OS 内核(RFC 2960)
通过用户级支持SCTP Sockets API Extensions
。
在开发过程中,在以下方面进行了测试:
- 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 with lksctp-tools-1.0.7
- FreeBSD 8.2
此模块是为一对多风格套接字(类型seqpacket
)编写的。除此之外peeloff/2
,stream
还引入了一对一风格的套接字(类型)。
该模块的记录定义可以使用以下方法找到:
-include_lib("kernel/include/inet_sctp.hrl").
无论底层 C API 使用哪种拼写方式,这些记录定义都使用“新”拼写“自适应”,而不是“弃用”“自适应”。
数据类型
assoc_id()
例如,返回一个不透明的术语,用于#sctp_paddr_change{}
标识SCTP套接字的关联。除了0
具有“全部端点”或“所有未来关联”等含义的特殊值之外,该术语是不透明的。
option() =
{active, true | false | once | -32768..32767} |
{buffer, integer() >= 0} |
{dontroute, boolean()} |
{high_msgq_watermark, integer() >= 1} |
{linger, {boolean(), integer() >= 0}} |
{low_msgq_watermark, integer() >= 1} |
{mode, list | binary} |
list |
binary |
{priority, integer() >= 0} |
{recbuf, integer() >= 0} |
{reuseaddr, boolean()} |
{ipv6_v6only, boolean()} |
{sctp_adaptation_layer, #sctp_setadaptation{}} |
{sctp_associnfo, #sctp_assocparams{}} |
{sctp_autoclose, integer() >= 0} |
{sctp_default_send_param, #sctp_sndrcvinfo{}} |
{sctp_delayed_ack_time, #sctp_assoc_value{}} |
{sctp_disable_fragments, boolean()} |
{sctp_events, #sctp_event_subscribe{}} |
{sctp_get_peer_addr_info, #sctp_paddrinfo{}} |
{sctp_i_want_mapped_v4_addr, boolean()} |
{sctp_initmsg, #sctp_initmsg{}} |
{sctp_maxseg, integer() >= 0} |
{sctp_nodelay, boolean()} |
{sctp_peer_addr_params, #sctp_paddrparams{}} |
{sctp_primary_addr, #sctp_prim{}} |
{sctp_rtoinfo, #sctp_rtoinfo{}} |
{sctp_set_peer_primary_addr, #sctp_setpeerprim{}} |
{sctp_status, #sctp_status{}} |
{sndbuf, integer() >= 0} |
{tos, integer() >= 0}
SCTP Socket Options
其中之一。
option_name() =
active |
buffer |
dontroute |
high_msgq_watermark |
linger |
low_msgq_watermark |
mode |
priority |
recbuf |
reuseaddr |
ipv6_v6only |
sctp_adaptation_layer |
sctp_associnfo |
sctp_autoclose |
sctp_default_send_param |
sctp_delayed_ack_time |
sctp_disable_fragments |
sctp_events |
sctp_get_peer_addr_info |
sctp_i_want_mapped_v4_addr |
sctp_initmsg |
sctp_maxseg |
sctp_nodelay |
sctp_peer_addr_params |
sctp_primary_addr |
sctp_rtoinfo |
sctp_set_peer_primary_addr |
sctp_status |
sndbuf |
tos
sctp_socket()
Socket 返回的标识符open/*
。
出口
abort(Socket,Assoc) - > ok | {error,inet:posix()}
类型
指定的关联异常终止。Assoc
,而不刷新未发送的数据。插座本身保持打开。在此套接字上打开的其他关联仍然有效,可以在新关联中使用该套接字。
关闭(Socket) - > ok | {error,inet:posix()}
类型
关闭套接字及其上的所有关联。未发送的数据被刷新eof/2
。该close/1
调用被阻塞或者取决于linger
套接字的值option
。如果close
不持久或延迟超时过期,则调用返回并且数据在后台刷新。
连接(套接字,地址,端口,选项) - >
{ok,Assoc} | {error,inet:posix()
}
类型
和connect(Socket, Addr, Port, Opts, infinity)
一样。
连接(套接字,地址,端口,选项,超时) - >
{ok,Assoc} | {error,inet:posix()
}
类型
Socket
使用Addr
和指定的对等体(SCTP 服务器套接字)为套接字建立新的关联Port
。Timeout
以毫秒表示。套接字可以与多个对等关联。
警告
如果使用的值Timeout
小于操作系统建立关联所花费的最大时间(如果使用默认值,RFC 4960
则大约需要4.5分钟)可能会导致返回值不一致或不正确。这与共享相同Socket
(即源地址和端口)的关联特别相关,因为控制进程阻塞直到connect/*
返回。connect_init/*
提供了没有这种限制的替代方案
结果connect/*
是一个#sctp_assoc_change{}
特别包含新事件的事件Association ID
:
#sctp_assoc_change{
state = atom(),
error = atom(),
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}}])
所有选项Opt
都在尝试关联之前在套接字上设置。如果选项记录具有未定义的字段值,则首先从套接字读取这些值的选项记录。实际上,Opt
选项记录仅在连接之前定义字段值以进行更改。
返回outbound_streams
并且inbound_streams
是套接字上的流号码。如果对等体要求较低的值,这些可以与请求的值不同(OutStreams
并且MaxInStreams
分别)。
state
可以具有以下值:
comm_up
协会成功建立。这表明一个成功的完成connect
。
cant_assoc
该关联不能建立(connect/*
失败)。
其他状态通常不会在输出中出现connect/*
。相反,它们可能发生在#sctp_assoc_change{}
接收的事件中,而不是recv/*
呼叫中的数据。所有这些都表明由于各种错误条件而失去了联系,并且为了完整起见在此列出:
comm_lost
restart
shutdown_comp
现场error
可以提供更详细的诊断。
connect_init(套接字,地址,端口,选项) - >
好的| {error,inet:posix()
}
类型
和connect_init(Socket, Addr, Port, Opts, infinity)
一样。
connect_init(套接字,地址,端口,选项,超时) - >
ok| {error,inet:posix()
}
类型
Socket
使用Addr
和指定的对等体(SCTP服务器套接字)为套接字启动一个新的关联Port
。
这个 API 的基本区别在于connect/*
返回值是底层 OS connect(2)
系统调用的返回值。如果ok
被返回,则关联建立的结果作为#sctp_assoc_change{}
事件被调用过程接收。调用过程必须准备好接收该调用过程,或recv/*
根据活动选项的值轮询使用它。
参数如所述connect/*
,但Timeout
值除外。
计时器Timeout
仅与监控IP解析度有关Addr
。
controls_process(Socket,Pid) - > ok | {错误,原因}
类型
分配一个新的控制过程Pid
来Socket
。与gen_udp:controlling_process/2
。相同。
eof(Socket,Assoc) - > ok | {错误,原因}
类型
正常结束所指定的关联Assoc
,并清除所有未发送的数据。插座本身保持打开状态。在此套接字上打开的其他关联仍然有效。该套接字可用于新的关联。
error_string(ErrorNumber) - > ok | string()| 未知错误
类型
将 SCTP 错误编号从例如#sctp_remote_error{}
或#sctp_send_failed{}
解释为解释性字符串或其中一个原子转换ok
为无错误或undefined
无法识别的错误。
监听(Socket,IsServer) - > ok | {错误,原因}
监听(Socket,Backlog) - > ok | {错误,原因}
类型
设置一个套接字来侦听它绑定到的 IP 地址和端口号。
对于类型seqpacket
,套接字(默认)IsServer
必须是true
或false
。与 TCP 相比,SCTP 中没有侦听队列长度。如果IsServer
是true
,则套接字接受新的关联,即它成为 SCTP 服务器套接字。
类型stream
,套接字待办事项处理将定义待办事项队列长度,就像在 TCP 中一样
open() - > {ok,Socket} | {error,inet:posix()}
打开(端口) - > {ok,Socket} | {error,inet:posix()}
打开(选项) - > {ok,Socket} | {error,inet:posix()}
打开(Port,Opts) - > {ok,Socket} | {error,inet:posix()}
类型
创建 SCTP 套接字并将其绑定到由所有{ip,IP}
(或同义{ifaddr,IP}
)选项指定的本地地址(此功能称为 SCTP 多宿主)。默认的IP
和Port
是any
和0
,无任何端口上绑定的意思向所有本地地址。
其他备选方案:
inet6
设置 IPv 6 的套接字。
inet
设置 IPv 4 的套接字。这是默认的。
使用一组默认的套接字options
。特别是,套接字以 SockType 和相当大的驱动程序binary
和passive
模式打开。seqpacketkernelbuffers
peeloff(Socket,Assoc) - > {ok,NewSocket} | {错误,原因}
类型
将类型(一对多样式)Assoc
的套接字Socket
中的现有关联分支到类型seqpacket
(一对一样式)的新套接字NewSocket
中stream
。
现有关联参数Assoc
可以是#sctp_assoc_change{}
记录,例如,从recv/*
,,,connect/*
,或者从处于活动模式的侦听套接字。它也可以是一个领域assoc_id
从这样的记录中提取的整数。
recv(套接字) - >
{ok,{FromIP,FromPort,AncData,Data}} | {错误,原因}
recv(套接字,超时) - >
{ok,{FromIP,FromPort,AncData,Data}} | {错误,原因}
类型
接收Data
来自套接字的任何关联的消息。如果接收超时,{error,timeout}
会被归还。默认的超时是infinity
...FromIP
和FromPort
指示发件人的地址。
AncData
是可以与主要一起接收的辅助数据项目的列表Data
。#sctp_sndrcvinfo{}
如果启用了此类辅助数据的接收,此列表可以为空或包含单个记录(请参阅选项sctp_events
)。它是默认启用的,因为这样的辅助数据提供了一种确定收到消息的关联和流的简单方法。(另一种方法是从套接字选项中获取关联 ID FromIP
并FromPort
使用套接字选项sctp_get_peer_addr_info
,但这仍不会产生流号码)。
所述Data
接收可以是一个binary()
或一个list()
(通过255的整数范围为0)的字节取决于插座模式或 SCTP 事件。
可能的 SCTP 事件:
#sctp_sndrcvinfo{}
#sctp_assoc_change{}
- #sctp_paddr_change {addr = {ip_address(),port()},state = atom(),error = integer(),assoc_id = assoc_id()}
指示addr
关联内指定的对等方 IP 地址的状态更改assoc_id
。state
(主要是不言自明的)的可能值包括:
addr_unreachable
addr_available
addr_removed
addr_added
addr_made_prim
addr_confirmed
如果出现错误(例如addr_unreachable
),则字段error
提供更多诊断。在这种情况下,事件#sctp_paddr_change{}
会自动转换为error
返回的字词recv
。可以使用error
字段值将其转换为字符串error_string/1
。
- #sctp_send_failed {
flags
= true | false,error
= integer(),info
= #sctp_sndrcvinfo
{},assoc_id = assoc_id()data
= binary()}如果发送操作失败,发件人可以收到此事件。flags
一个布尔值,指定数据是否通过导线传输。error
提供扩展诊断,使用失败时使用error_string/1.
info
的原始记录 尝试发送整个原始数据块。在Erlang / SCTP绑定的当前实现中,该事件在内部被转换为返回的术语。#sctp_sndrcvinfo{}send/*.
dataerrorrecv/*
- #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绑定的当前实现中,该事件在内部被转换error为由recv/*.send(Socket,SndRcvInfo,Data)返回的术语- > ok | {error,Reason} Types Data从#sctp_sndrcvinfo{}记录中发送带有所有发送参数的消息。通过这种方式,用户可以指定 PPID(传递给远端)和上下文(传递给本地 SCTP 层),例如,可用于识别错误。但是,很少需要这样好的用户控制级别。该函数send/4对于大多数applications.send(Socket,Assoc,Stream,Data) - > ok |都是足够的 {错误,原因} TypesSends一个Data消息通过现有的关联和指定的流。SCTP 套接字选项允许的SCTP套接字选项的集合通过与 TCP,UDP 和通用inet选项集构造正交。此处列出的选项仅适用于 SCTP 套接字。可以使用open/1,2或在使用inet:setopts/2检索的套接字上设置选项inet:getopts/2。呼叫时可以更改选项connect/4,5。或者只是或确定从中返回的数据的类型。{mode, list|binary} list binaryrecv/1,2{active, true|false|once|N}
- 如果
false
(被动模式,默认),调用者必须执行显式recv
调用才能从套接字中检索可用数据。
- 如果
true
(完全激活模式),未决数据或事件被发送到拥有进程。请注意,这可能会导致消息队列溢出,因为在这种情况下无法限制发件人(无流量控制)。
- 如果
once
,只会自动将一条消息放置在消息队列中,然后自动将模式重置为被动模式。这为接收方提供了流控制和侦听其与其他进程间消息交织的 SCTP 数据的可能性。
- 如果
active
指定为N
-32768到32767(含)范围内的整数,则该数字将被添加到套接字对传递到控制进程的数据消息的计数中。如果加法结果是否定的,则计数设置为0
。一旦计数到达0
,无论是通过传递消息还是通过明确设置inet:setopts/2
,套接字模式都将自动重置为passive({active, false}
)。当处于主动模式的套接字转换到被动模式时,消息{sctp_passive, Socket}
被发送到控制进程以通知它,如果它想要从套接字接收更多的数据消息,则它必须调用inet:setopts/2
以将套接字重新设置为活动模式。
{tos, integer()}
将发送的 IP 数据报上的服务类型字段设置为指定的值。这有效地确定出站数据包的优先级策略。可接受的值取决于系统。
{priority, integer()}
上面的协议无关等价物tos
。设置优先级也意味着设置tos
。
{dontroute, true|false}
默认为false
。如果true
内核不通过任何网关发送数据包,则只将它们发送给直接连接的主机。
{reuseaddr, true|false}
默认为false
。如果为 true,则{IP,Port}
套接字的本地绑定地址可以立即重新使用。没有CLOSE_WAIT
执行状态等待(高吞吐量服务器可能需要)。
{sndbuf, integer()}
此套接字的 OS 内核发送缓冲区的大小(以字节为单位)。发送错误的数据报大于val(sndbuf)
。设置该选项还可以调整驱动程序缓冲区的大小(请参阅buffer
上文)。
{recbuf, integer()}
此套接字的 OS 内核接收缓冲区的大小(以字节为单位)。发送错误的数据报大于val(recbuf)
。设置该选项还可以调整驱动程序缓冲区的大小(请参阅buffer
上文)。
{sctp_module, module()}
覆盖使用哪个回调模块。默认inet_sctp
为 IPv4 和inet6_sctp
IPv6。
{sctp_rtoinfo, #sctp_rtoinfo{}}
#sctp_rtoinfo{
assoc_id = assoc_id(),
initial = integer(),
max = integer(),
min = integer()
}
确定通过指定的关联的重新传输超时参数(以毫秒为单位)assoc_id
。
assoc_id = 0
(默认)表示整个端点。见RFC 2960
与Sockets API Extensions for SCTP
字段值的准确语义。
{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
(默认)表示整个端点。请参阅Sockets API Extensions for SCTP
其语义讨论。几乎没有使用过。
{sctp_initmsg, #sctp_initmsg{}}
#sctp_initmsg{
num_ostreams = integer(),
max_instreams = integer(),
max_attempts = integer(),
max_init_timeo = integer()
}
确定此套接字在与其建立关联时尝试与其对等方进行协商的默认参数。将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 地址的自动映射(如果套接字地址族是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
作为建立新协会的适应指示参数。
{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{}
在此套接字选项中使用,并在发送或接收SCTP消息时作为辅助数据使用。当设置为选项时,它send
为由指定的关联后续调用提供默认值assoc_id
。
assoc_id = 0
(默认)表示整个端点。
发送方通常必须指定下列字段:
sinfo_stream
流号码(0-base)在关联内发送消息;
sinfo_flags
下列旗帜得到承认:
unordered
这条消息将被无序发送。addr_over
中指定的地址。send
覆盖主对等地址abort
中止当前关联而不刷新任何未发送的数据。eof
优雅地关闭当前的关联,同时刷新未发送的数据。
其他领域很少使用。
{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 Events
数据将与(通过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()
}
几乎没有使用过。确定assoc_value
指定关联或整个端点的ACK时间(以毫秒为单位)assoc_value = 0
(默认值)。
{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{}
}
该选项是只读的。它确定由指定的SCTP关联的状态assoc_id
。以下是可能的值state
(州名称大多是不言自明的):
sctp_state_empty
默认。意味着没有其他状态处于活动状态。sctp_state_closedsctp_state_cookie_waitsctp_state_cookie_echoedsctp_state_establishedsctp_state_shutdown_pendingsctp_state_shutdown_sentsctp_state_shutdown_receivedsctp_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。这个选项很少使用。有关所有字段的语义,
SCTP 实例
Erlang SCTP 服务器接收 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个出站流。因此,发送"Test 0"
超过流0成功,但发送"Test 5"
过流5失败。那么客户abort
s关联,结果是在服务器端接收到相应的事件。
-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).
一个简单的 ErlangSCTP 客户端,它使用connect_init
API:
-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(3)
,gen_udp(3)
,inet(3)
,RFC 2960
(流控制传输协议),Sockets API Extensions for SCTP