非常教程

Erlang 20参考手册

核心 | kernel

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/2stream还引入了一对一风格的套接字(类型)。

该模块的记录定义可以使用以下方法找到:

-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 服务器套接字)为套接字建立新的关联PortTimeout以毫秒表示。套接字可以与多个对等关联。

警告

如果使用的值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_lostrestartshutdown_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 | {错误,原因}

类型

分配一个新的控制过程PidSocket。与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必须是truefalse。与 TCP 相比,SCTP 中没有侦听队列长度。如果IsServertrue,则套接字接受新的关联,即它成为 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 多宿主)。默认的IPPortany0,无任何端口上绑定的意思向所有本地地址。

其他备选方案:

inet6

设置 IPv 6 的套接字。

inet

设置 IPv 4 的套接字。这是默认的。

使用一组默认的套接字options。特别是,套接字以 SockType 和相当大的驱动程序binarypassive模式打开。seqpacketkernelbuffers

peeloff(Socket,Assoc) - > {ok,NewSocket} | {错误,原因}

类型

将类型(一对多样式)Assoc的套接字Socket中的现有关联分支到类型seqpacket(一对一样式)的新套接字NewSocketstream

现有关联参数Assoc可以是#sctp_assoc_change{}记录,例如,从recv/*,,,connect/*,或者从处于活动模式的侦听套接字。它也可以是一个领域assoc_id从这样的记录中提取的整数。

recv(套接字) - >

{ok,{FromIP,FromPort,AncData,Data}} | {错误,原因}

recv(套接字,超时) - >

{ok,{FromIP,FromPort,AncData,Data}} | {错误,原因}

类型

接收Data来自套接字的任何关联的消息。如果接收超时,{error,timeout}会被归还。默认的超时是infinity...FromIPFromPort指示发件人的地址。

AncData是可以与主要一起接收的辅助数据项目的列表Data#sctp_sndrcvinfo{}如果启用了此类辅助数据的接收,此列表可以为空或包含单个记录(请参阅选项sctp_events)。它是默认启用的,因为这样的辅助数据提供了一种确定收到消息的关联和流的简单方法。(另一种方法是从套接字选项中获取关联 ID FromIPFromPort使用套接字选项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_idstate(主要是不言自明的)的可能值包括:

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 2960Sockets 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失败。那么客户aborts关联,结果是在服务器端接收到相应的事件。

-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_initAPI:

-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

Erlang 20

Erlang 是一种通用的面向并发的编程语言,可应付大规模开发活动的程序设计语言和运行环境。

主页 https://www.erlang.org/
源码 https://github.com/erlang/otp
版本 20
发布版本 20.1