编程接口指南

流控制传输协议

流控制传输协议 (Stream Control Transmission Protocol, SCTP) 是一种可靠的传输协议,提供的服务与 TCP 提供的服务类似。此外,SCTP 还提供网络级别的容错功能。SCTP 支持关联两端具有多宿主。SCTP 套接字 API 支持模仿 TCP 的一对一的套接字风格。SCTP 套接字 API 还支持旨在用于发送信号的一对多套接字风格。一对多套接字风格可以减少在进程中使用的文件描述符数。必须链接 libsctp 库才能使用 SCTP 函数调用。

将在两个端点之间设置 SCTP 关联。这些端点使用的四次握手机制借助 cookie 来防止遭到某些类型的拒绝服务 (denial-of-service, DoS) 攻击。端点可由多个 IP 地址表示。

SCTP 套接字接口

socket() 调用为 IPPROTO_SCTP 创建套接字时,它会调用特定于 SCTP 的套接字创建例程。针对 SCTP 套接字执行的套接字调用会自动调用相应的 SCTP 套接字例程。在一对一套接字中,每个套接字都对应一个 SCTP 关联。可以通过调用以下函数来创建一对一套接字:

socket(AF_INET[6], SOCK_STREAM, IPPROTO_STCP);

在一对多风格套接字中,每个套接字都处理多个 SCTP 关联。每个关联都具有一个名为 sctp_assoc_t 的关联标识符。可以通过调用以下函数来创建一对多套接字:

socket(AF_INET[6], SOCK_SEQPACKET, IPPROTO_STCP);

sctp_bindx()

int sctp_bindx(int sock, void *addrs, int addrcnt, int flags);

sctp_bindx() 函数管理 SCTP 套接字上的地址。如果 sock 参数为 IPv4 套接字,则传送给 sctp_bindx() 函数的地址必须为 IPv4 地址。如果 sock 参数为 IPv6 套接字,则传送给 sctp_bindx() 函数的地址可以为 IPv4 或 IPv6 地址。当传送给 sctp_bindx() 函数的地址为 INADDR_ANYIN6ADDR_ANY 时,此套接字将绑定到所有可用地址。可以使用 bind(3SOCKET) 绑定 SCTP 端点。

*addrs 参数的值是指向包含一个或多个套接字地址的数组的指针。每个地址都包含在其相应的结构中。如果地址为 IPv4 地址,则它们可以包含在 sockaddr_in 结构或 sockaddr_in6 结构中。如果地址为 IPv6 地址,则它们可以包含在 sockaddr_in6 结构中。可以通过地址类型系列区分地址长度。调用方使用 addrcnt 参数指定数组中的地址数。

如果成功,则 sctp_bindx() 函数将返回 0。如果失败,则 sctp_bindx() 函数将返回 -1,并将 errno 的值设置为相应的错误代码。

如果没有为每个套接字地址提供同一端口,则 sctp_bindx() 函数将失败,并将 errno 的值设置为 EINVAL

通过对以下零个或多个当前定义的标志执行按位 OR 运算,即可形成 flags 参数:

SCTP_BINDX_ADD_ADDR 指示 SCTP 将给定地址添加到关联中。SCTP_BINDX_REM_ADDR 指示 SCTP 从关联中删除给定地址。这两个标志相互排斥。如果同时提供这两个标志,则 sctp_bindx() 将失败,并将 errno 的值设置为 EINVAL

调用方无法删除关联中的所有地址。sctp_bindx() 函数拒绝此类尝试的表现为:函数失败并将 errno 的值设置为 EINVAL。应用程序可以在调用 bind() 函数之后使用 sctp_bindx(SCTP_BINDX_ADD_ADDR),将其他地址与端点关联。应用程序可以使用 sctp_bindx(SCTP_BINDX_REM_ADDR) 删除与侦听套接字关联的地址。使用 sctp_bindx(SCTP_BINDX_REM_ADDR) 删除地址之后,接受新关联将不会重新关联已删除的地址。如果端点支持动态地址,则可以使用 SCTP_BINDX_REM_ADDRSCTP_BINDX_ADD_ADDR 向对等方发送消息来更改对等方的地址列表。在已连接的关联中添加和删除地址为可选功能。不支持此功能的实现将返回 EOPNOTSUPP

如果地址族不是 AF_INETAF_INET6,则 sctp_bindx() 函数将失败并返回 EAFNOSUPPORT。如果 sock 参数中传递给 sctp_bindx() 的文件描述符无效,则 sctp_bindx() 函数将失败并返回 EBADF

sctp_opt_info()

int sctp_opt_info(int sock, sctp_assoc_id_t id, int opt, void *arg, socklen_t *len);

sctp_opt_info() 函数将返回与 sock 参数中所述的套接字关联的 SCTP 级别选项。如果此套接字为一对多风格 SCTP 套接字,则 id 参数的值是某个特定关联。对于一对一风格 SCTP 套接字,将忽略 id 参数。opt 参数的值指定要获取的 SCTP 套接字选项。arg 参数的值是为调用程序而分配的特定于选项的结构缓冲区。*len 参数的值为选项长度。

opt 参数可以采用以下值:

SCTP_RTOINFO

返回用于初始化和绑定重新传输超时 (retransmission timeout, RTO) 可调参数的协议参数。这些协议参数使用以下结构:

struct sctp_rtoinfo {

       sctp_assoc_t srto_assoc_id;

       uint32_t     srto_initial;

       uint32_t     srto_max; 

       uint32_t     srto_min;

};
srto_assoc_id

调用程序提供此值,它指定所关注的关联。

srto_initial

此值为初始 RTO 值。

srto_max

此值为最大 RTO 值。

srto_min

此值为最小 RTO 值。

SCTP_ASSOCINFO

返回特定于关联的参数。这些参数使用以下结构:

struct sctp_assocparams {

     sctp_assoc_t sasoc_assoc_id;

     uint16_t     sasoc_asocmaxrxt;

     uint16_t     sasoc_number_peer_destinations;

     uint32_t     sasoc_peer_rwnd;

     uint32_t     sasoc_local_rwnd;

     uint32_t     sasoc_cookie_life;

};
sasoc_assoc_id

调用程序提供此值,它指定所关注的关联。

sasoc_assocmaxrxt

此值指定关联的最大重新传输计数。

sasoc_number_peer_destinations

此值指定对等方具有的地址数。

sasoc_peer_rwnd

此值指定对等方接收窗口的当前值。

sasoc_local_rwnd

此值指定对等方传输到的上一个已报告的接收窗口。

sasoc_cookie_life

此值指定关联 cookie 的生命周期。可在发出 cookie 时使用此值。

所有使用时间值的参数均以毫秒为单位。

SCTP_DEFAULT_SEND_PARAM

返回 sendto(3SOCKET) 函数调用在此关联中使用的缺省参数集。这些参数使用以下结构:

struct sctp_sndrcvinfo {

     uint16_t     sinfo_stream;

     uint16_t     sinfo_ssn;

     uint16_t     sinfo_flags;

     uint32_t     sinfo_ppid;

     uint32_t     sinfo_context;

     uint32_t     sinfo_timetolive;

     uint32_t     sinfo_tsn;

     uint32_t     sinfo_cumtsn;

     sctp_assoc_t sinfo_assoc_id;

};
sinfo_stream

此值指定 sendmsg() 调用的缺省流。

sinfo_ssn

此值始终为 0。

sinfo_flags

此值包含 sendmsg() 调用的缺省标志。此标志可以采用以下值:

  • MSG_UNORDERED

  • MSG_ADDR_OVER

  • MSG_ABORT

  • MSG_EOF

  • MSG_PR_SCTP

sinfo_ppid

此值为 sendmsg() 调用的缺省有效负荷协议标识符。

sinfo_context

此值为 sendmsg() 调用的缺省上下文。

sinfo_timetolive

此值指定时间段(以毫秒为单位)。在此时间段过后,如果消息传输尚未开始,则消息将过期。值为 0 指示消息尚未过期。如果设置了 MSG_PR_SCTP 标志,当消息传输未在 sinfo_timetolive 所指定的时间段内成功完成时,消息将过期。

sinfo_tsn

此值始终为 0。

sinfo_cumtsn

此值始终为 0。

sinfo_assoc_id

此值由调用程序填充。它指定所关注的关联。

SCTP_PEER_ADDR_PARAMS

返回所指定对等地址的参数。这些参数使用以下结构:

struct sctp_paddrparams {

     sctp_assoc_t            spp_assoc_id;

     struct sockaddr_storage spp_address;

     uint32_t                spp_hbinterval;

     uint16_t                spp_pathmaxrxt;

};
spp_assoc_id

调用程序提供此值,它指定所关注的关联。

spp_address

此值指定所关注的对等地址。

spp_hbinterval

此值指定心跳间隔(以毫秒为单位)。

spp_pathmaxrxt

此值指定在认为地址不可访问之前针对此地址尝试的最大重新传输数。

SCTP_STATUS

返回有关关联的当前状态信息。这些参数使用以下结构:

struct sctp_status {

     sctp_assoc_t          sstat_assoc_id;

     int32_t               sstat_state;

     uint32_t              sstat_rwnd;

     uint16_t              sstat_unackdata;

     uint16_t              sstat_penddata;

     uint16_t              sstat_instrms;

     uint16_t              sstat_outstrms;

     uint32_t              sstat_fragmentation_point;

     struct sctp_paddrinfo sstat_primary;

};
sstat_assoc_id

调用程序提供此值,它指定所关注的关联。

sstat_state

此值为关联的当前状态。关联可以采用以下状态:

SCTP_IDLE

SCTP 端点没有任何与其关联的关联。一旦 socket() 函数调用打开一个端点或端点关闭,则端点便会处于此状态。

SCTP_BOUND

SCTP 端点在调用 bind() 之后绑定到一个或多个本地地址。

SCTP_LISTEN

此端点在等待来自任何远程 SCTP 端点的关联请求。

SCTP_COOKIE_WAIT

此 SCTP 端点已发送 INIT 块并在等待 INIT-ACK 块。

SCTP_COOKIE_ECHOED

此 SCTP 端点已将从其对等方的 INIT-ACK 块接收的 cookie 回显到对等方。

SCTP_ESTABLISHED

此 SCTP 端点可以与其对等方交换数据。

SCTP_SHUTDOWN_PENDING

此 SCTP 端点已从其上层接收了 SHUTDOWN 元语。此端点不再从其上层接受数据。

SCTP_SHUTDOWN_SEND

处于 SCTP_SHUTDOWN_PENDING 状态的 SCTP 端点已向其对等方发送了 SHUTDOWN 块。仅在确认所有从此端点到其对等方的未完成数据之后,才发送 SHUTDOWN 块。当此端点的对等方发送 SHUTDOWN ACK 块时,此端点会发送 SHUTDOWN COMPLETE 块并认为关联已关闭。

SCTP_SHUTDOWN_RECEIVED

SCTP 端点已从其对等方接收了 SHUTDOWN 块。此端点不再从其用户接受新数据。

SCTP_SHUTDOWN_ACK_SEND

处于 SCTP_SHUTDOWN_RECEIVED 状态的 SCTP 端点已向其对等方发送了 SHUTDOWN ACK 块。此端点仅在其对等方确认来自此端点的所有未完成数据之后发送 SHUTDOWN ACK 块。当此端点的对等方发送 SHUTDOWN COMPLETE 块时,将关闭关联。

sstat_rwnd

此值为关联对等方的当前接收窗口。

sstat_unackdata

此值为未确认的 DATA 块数。

sstat_penddata

此值为等待接收的 DATA 块数。

sstat_instrms

此值为传入的流数。

sstat_outstrms

此值为外发的流数。

sstat_fragmentation_point

如果消息、SCTP 数据包头和 IP 数据包头的组合大小超出 sstat_fragmentation_point 的值,则消息会分段。此值等于包目标地址的路径最大传输单元 (Path Maximum Transmission Unit, P-MTU)。

sstat_primary

此值包含有关主要对等地址的信息。此信息使用以下结构:

struct sctp_paddrinfo {

     sctp_assoc_t            spinfo_assoc_id;

     struct sockaddr_storage spinfo_address;

     int32_t                 spinfo_state;

     uint32_t                spinfo_cwnd;

     uint32_t                spinfo_srtt;

     uint32_t                spinfo_rto;

     uint32_t                spinfo_mtu;

};
spinfo_assoc_id

调用程序提供此值,它指定所关注的关联。

spinfo_address

此值为主要对等地址。

spinfo_state

此值可以采用 SCTP_ACTIVESCTP_INACTIVE 两个值中的任意一个。

spinfo_cwnd

此值为对等地址的拥塞窗口。

spinfo_srtt

此值为对等地址的当前平滑往返时间计算结果,以毫秒为单位。

spinfo_rto

此值为对等地址的当前重新传输超时值,以毫秒为单位。

spinfo_mtu

此值为对等地址的 P-MTU。

如果成功,则 sctp_opt_info() 函数将返回 0。如果失败,则 sctp_opt_info() 函数将返回 -1,并将 errno 的值设置为相应的错误代码。如果 sock 参数中传递给 sctp_opt_info() 的文件描述符无效,则 sctp_opt_info() 函数将失败并返回 EBADF。如果 sock 参数中传递给 sctp_opt_info() 函数的文件描述符没有描述套接字,则 sctp_opt_info() 函数将失败并返回 ENOTSOCK。如果关联 ID 对于一对多风格 SCTP 套接字而言无效,则 sctp_opt_info() 函数将失败,并将 errno 的值设置为 EINVAL。如果输入缓冲区长度对于指定的选项而言过短,则 sctp_opt_info() 函数将失败,并将 errno 的值设置为 EINVAL。如果对等地址的地址族不是 AF_INETAF_INET6,则 sctp_opt_info() 函数将失败,并将 errno 的值设置为 EAFNOSUPPORT

sctp_recvmsg()

ssize_t sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from, socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags);

使用 sctp_recvmsg() 函数,可以从 s 参数所指定的 SCTP 端点接收消息。调用程序可以指定以下属性:

msg

此参数为消息缓冲区的地址。

len

此参数为消息缓冲区的长度。

from

此参数为指向包含发送主机地址的地址的指针。

fromlen

此参数为与 from 参数中的地址关联的缓冲区的大小。

sinfo

此参数仅在调用程序启用 sctp_data_io_events 时处于活动状态。要启用 sctp_data_io_events,请使用套接字选项 SCTP_EVENTS 调用 setsockopt() 函数。如果启用了 sctp_data_io_events,则应用程序将接收每个传入消息的 sctp_sndrcvinfo 结构的内容。此参数为指向 sctp_sndrcvinfo 结构的指针。此结构将在接收消息时进行填充。

msg_flags

此参数包含所有存在的消息标志。

sctp_recvmsg() 函数将返回其接收的字节数。sctp_recvmsg() 函数将在出现错误时返回 -1。

如果在 s 参数中传递的文件描述符无效,则 sctp_recvmsg() 函数将失败,并将 errno 的值设置为 EBADF。如果在 s 参数中传递的文件描述符没有描述套接字,则 sctp_recvmsg() 函数将失败,并将 errno 的值设置为 ENOTSOCK。如果 msg_flags 参数包括值 MSG_OOB,则 sctp_recvmsg() 函数将失败,并将 errno 的值设置为 EOPNOTSUPP。如果没有建立关联,则 sctp_recvmsg() 函数将失败,并将 errno 的值设置为 ENOTCONN

sctp_sendmsg()

ssize_t sctp_sendmsg(int s, const void *msg, size_t len, const struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream_no, uint32_t timetolive, uint32_t context);

sctp_sendmsg() 函数在发送来自 SCTP 端点的消息时启用高级 SCTP 功能。

s

此值指定发送消息的 SCTP 端点。

msg

此值包含 sctp_sendmsg() 函数所发送的消息。

len

此值为消息的长度,以字节为单位。

to

此值为消息的目标地址。

tolen

此值为目标地址的长度。

ppid

此值为应用程序指定的有效负荷协议标识符。

stream_no

此值为此消息的目标流。

timetolive

此值为消息未能成功发送到对等方的情况下消息过期之前可以等待的时间段,以毫秒为单位。

context

如果在发送消息时出现错误,则返回此值。

flags

此值在将逻辑运算 OR 以按位形式应用于以下零个或多个标志位时形成:

MSG_UNORDERED

设置此标志之后,sctp_sendmsg() 函数将无序传送消息。

MSG_ADDR_OVER

设置此标志之后,sctp_sendmsg() 函数将使用 to 参数中的地址,而不使用关联的主要目标地址。此标志仅用于一对多风格 SCTP 套接字。

MSG_ABORT

设置此标志之后,指定的关联将异常中止,同时向其对等方发送 ABORT 信号。此标志仅用于一对多风格 SCTP 套接字。

MSG_EOF

设置此标志之后,指定的关联将进入正常关机状态。此标志仅用于一对多风格 SCTP 套接字。

MSG_PR_SCTP

设置此标志之后,如果消息传输未在 timetolive 参数所指定的时间段内成功完成,则消息将过期。

sctp_sendmsg() 函数将返回其发送的字节数。sctp_sendmsg() 函数将在出现错误时返回 -1。

如果在 s 参数中传递的文件描述符无效,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EBADF。如果在 s 参数中传递的文件描述符没有描述套接字,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 ENOTSOCK。如果 flags 参数包括值 MSG_OOB,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EOPNOTSUPP。如果一对一风格套接字的 flags 参数包括 MSG_ABORTMSG_EOF 值,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EOPNOTSUPP。如果没有建立关联,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 ENOTCONN。如果套接字关闭,禁止进一步写入,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EPIPE。如果套接字为非阻止套接字并且传输队列已满,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EAGAIN

如果控制消息长度不正确,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EINVAL。如果指定的目标地址不属于关联,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EINVAL。如果 stream_no 的值不在关联所支持的外发流数之内,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EINVAL。如果所指定的目标地址的地址族不是 AF_INETAF_INET6,则 sctp_sendmsg() 函数将失败,并将 errno 的值设置为 EINVAL

sctp_send()

ssize_t sctp_send(int s, const void *msg, size_t len, const struct sctp_sndrcvinfo *sinfo, int flags);

sctp_send() 函数可供一对一及一对多风格套接字使用。sctp_send() 函数在发送来自 SCTP 端点的消息时启用高级 SCTP 功能。

s

此值指定 socket() 函数所创建的套接字。

msg

此值包含 sctp_send() 函数所发送的消息。

len

此值为消息的长度,以字节为单位。

sinfo

此值包含用于发送消息的参数。对于一对多风格套接字,此值可以包含消息所发送到的关联 ID。

flags

此值与 sendmsg() 函数中的标志参数相同。

sctp_send() 函数将返回其发送的字节数。sctp_send() 函数将在出现错误时返回 -1。

如果在 s 参数中传递的文件描述符无效,则 sctp_send() 函数将失败,并将 errno 的值设置为 EBADF。如果在 s 参数中传递的文件描述符没有描述套接字,则 sctp_send() 函数将失败,并将 errno 的值设置为 ENOTSOCK。如果 sinfo 参数的 sinfo_flags 字段包括值 MSG_OOB,则 sctp_send() 函数将失败,并将 errno 的值设置为 EOPNOTSUPP。如果一对一风格套接字中 sinfo 参数的 sinfo_flags 字段包括 MSG_ABORTMSG_EOF 值,则 sctp_send() 函数将失败,并将 errno 值的设置为 EOPNOTSUPP。如果没有建立关联,则 sctp_send() 函数将失败,并将 errno 的值设置为 ENOTCONN。如果套接字关闭,禁止进一步写入,则 sctp_send() 函数将失败,并将 errno 的值设置为 EPIPE。如果套接字为非阻止套接字并且传输队列已满,则 sctp_send() 函数将失败,并将 errno 的值设置为 EAGAIN

如果控制消息长度不正确,则 sctp_send() 函数将失败,并将 errno 的值设置为 EINVAL。如果指定的目标地址不属于关联,则 sctp_send() 函数将失败,并将 errno 的值设置为 EINVAL。如果 stream_no 的值不在关联所支持的外发流数之内,则 sctp_send() 函数将失败,并将 errno 的值设置为 EINVAL。如果所指定的目标地址的地址族不是 AF_INETAF_INET6,则 sctp_send() 函数将失败,并将 errno 的值设置为 EINVAL

分叉关联

应用程序可以将一对多风格套接字上已建立的关联分叉为独立的套接字和文件描述符。对于具有多个偶发消息发送者或接收者的应用程序,如果这些发送者或接收者需要存在于原始一对多风格套接字之下,则独立的套接字和文件描述符非常有用。应用程序会将传输大量数据通信流量的关联分叉为独立的套接字描述符。应用程序使用 sctp_peeloff() 调用将关联分叉为独立的套接字。新套接字为一对一风格套接字。sctp_peeloff() 函数的语法如下:

int sctp_peeloff(int sock, sctp_assoc_t id);
sock

socket() 系统调用返回的原始一对多风格套接字描述符

id

要分叉为独立的文件描述符的关联的标识符

如果在 sock 参数中传递的套接字描述符不是一对多风格 SCTP 套接字,则 sctp_peeloff() 函数将失败并返回 EOPTNOTSUPP。如果 id 的值为 0 或者 id 的值大于在 sock 参数中传递的套接字描述符的最大关联数,则 sctp_peeloff() 函数将失败并返回 EINVAL。如果 sctp_peeloff() 函数无法创建新的用户文件描述符或文件结构,则此函数将失败并返回 EMFILE

sctp_getpaddrs()

sctp_getpaddrs() 函数将返回关联中的所有对等地址。

int sctp_getpaddrs(int sock, sctp_assoc_t id, void **addrs);

sctp_getpaddrs() 函数成功返回时,**addrs 参数的值将指向每个地址相应类型的动态分配的压缩 sockaddr 结构数组。调用线程使用 sctp_freepaddrs() 函数释放内存。**addrs 参数的值不能为 NULL。如果 sock 中给定的套接字描述符用于 IPv4 套接字,则 sctp_getpaddrs() 函数将返回 IPv4 地址。如果 sock 中给定的套接字描述符用于 IPv6 套接字,则 sctp_getpaddrs() 函数将同时返回 IPv4 和 IPv6 地址。对于一对多风格套接字,id 参数指定要查询的关联。对于一对一风格套接字,sctp_getpaddrs() 函数将忽略 id 参数。当 sctp_getpaddrs() 函数成功返回时,它将返回关联中的对等地址数。如果此套接字上没有关联,则 sctp_getpaddrs() 函数将返回 0,并且不定义 **addrs 参数的值。如果出现错误,则 sctp_getpaddrs() 函数将返回 -1,并且不定义 **addrs 参数的值。

如果 sock 参数中传递给 sctp_getpaddrs() 函数的文件描述符无效,则 sctp_getpaddrs() 函数将失败并返回 EBADF。如果 sock 参数中传递给 sctp_getpaddrs() 函数的文件描述符没有描述套接字,则 sctp_getpaddrs() 函数将失败并返回 ENOTSOCK。如果 sock 参数中传递给 sctp_getpaddrs() 函数的文件描述符描述了未连接的套接字,则 sctp_getpaddrs() 函数将失败并返回 ENOTCONN

sctp_freepaddrs()

sctp_freepaddrs() 函数将释放所有由之前的 sctp_getpaddrs() 调用所分配的资源。sctp_freepaddrs() 函数的语法如下:

void sctp_freepaddrs(void *addrs);

*addrs 参数为包含 sctp_getpaddrs() 函数所返回的对等地址的数组。

sctp_getladdrs()

sctp_getladdrs() 函数将返回套接字上的所有本地绑定的地址。sctp_getladdrs() 函数的语法如下:

int sctp_getladdrs(int sock, sctp_assoc_t id, void **addrs);

sctp_getladdrs() 函数成功返回时,addrs 的值将指向动态分配的压缩 sockaddr 结构数组。sockaddr 结构为每个本地地址的相应类型。调用应用程序使用 sctp_freeladdrs() 函数释放内存。addrs 参数的值不能为 NULL。

如果 sd 参数引用的套接字为 IPv4 套接字,则 sctp_getladdrs() 函数将返回 IPv4 地址。如果 sd 参数引用的套接字为 IPv6 套接字,则 sctp_getladdrs() 函数将同时返回相应的 IPv4 或 IPv6 地址。

针对一对多风格套接字调用 sctp_getladdrs() 函数时,id 参数的值指定要查询的关联。sctp_getladdrs() 函数在一对一套接字上运行时将忽略 id 参数。

id 参数的值为 0 时,无论为何种特定关联,sctp_getladdrs() 函数都将返回本地绑定的地址。当 sctp_getladdrs() 函数成功返回时,它将报告绑定到套接字的本地地址数。如果未绑定套接字,则 sctp_getladdrs() 函数将返回 0,并且不定义 *addrs 的值。如果出现错误,则 sctp_getladdrs() 函数将返回 -1,并且不定义 *addrs 的值。

sctp_freeladdrs()

sctp_freeladdrs() 函数将释放所有由之前的 sctp_getladdrs() 调用所分配的资源。sctp_freeladdrs() 函数的语法如下:

void sctp_freeladdrs(void *addrs);

*addrs 参数为包含 sctp_getladdrs() 函数所返回的对等地址的数组。

SCTP 用法代码示例

本节详细介绍 SCTP 套接字的两种用法。


示例 7–17 SCTP 回显客户机

/*

 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.

 * Use is subject to license terms.

 */



/* To enable socket features used for SCTP socket. */

#define _XPG4_2

#define __EXTENSIONS__



#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

#include <netinet/sctp.h>

#include <netdb.h>



#define BUFLEN 1024



static void

usage(char *a0)

{

 fprintf(stderr, "Usage: %s <addr>\n", a0);

}



/*

 * Read from the network.

 */

static void

readit(void *vfdp)

{

 int   fd;

 ssize_t   n;

 char   buf[BUFLEN];

 struct msghdr  msg[1];

 struct iovec  iov[1];

 struct cmsghdr  *cmsg;

 struct sctp_sndrcvinfo *sri;

 char   cbuf[sizeof (*cmsg) + sizeof (*sri)];

 union sctp_notification *snp;



 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);



 fd = *(int *)vfdp;



 /* Initialize the message header for receiving */

 memset(msg, 0, sizeof (*msg));

 msg->msg_control = cbuf;

 msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);

 msg->msg_flags = 0;

 cmsg = (struct cmsghdr *)cbuf;

 sri = (struct sctp_sndrcvinfo *)(cmsg + 1);

 iov->iov_base = buf;

 iov->iov_len = BUFLEN;

 msg->msg_iov = iov;

 msg->msg_iovlen = 1;



 while ((n = recvmsg(fd, msg, 0)) > 0) {

  /* Intercept notifications here */

  if (msg->msg_flags & MSG_NOTIFICATION) {

   snp = (union sctp_notification *)buf;

   printf("[ Receive notification type %u ]\n",

       snp->sn_type);

   continue;

  }

  msg->msg_control = cbuf;

  msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);

  printf("[ Receive echo (%u bytes): stream = %hu, ssn = %hu, "

      "flags = %hx, ppid = %u ]\n", n,

      sri->sinfo_stream, sri->sinfo_ssn, sri->sinfo_flags,

      sri->sinfo_ppid);

 }



 if (n < 0) {

     perror("recv");

     exit(1);

 }



 close(fd);

 exit(0);

}



#define MAX_STREAM 64



/*

 *

 */

static void

echo(struct sockaddr_in *addr)

{

 int     fd;

 uchar_t     buf[BUFLEN];

 ssize_t    n;

 int    perr;

 pthread_t   tid;

 struct cmsghdr   *cmsg;

 struct sctp_sndrcvinfo  *sri;

 char    cbuf[sizeof (*cmsg) + sizeof (*sri)];

 struct msghdr   msg[1];

 struct iovec   iov[1];

 int    ret;

 struct sctp_initmsg   initmsg;

 struct sctp_event_subscribe events;



 /* Create a one-one SCTP socket */

 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {

  perror("socket");

  exit(1);

 }



 /*

  * We are interested in association change events and we want

  * to get sctp_sndrcvinfo in each receive.

  */

 events.sctp_association_event = 1; 

 events.sctp_data_io_event = 1; 

 ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &events,

     sizeof (events));

 if (ret < 0) {

  perror("setsockopt SCTP_EVENTS");

  exit(1);

 }



 /*

  * Set the SCTP stream parameters to tell the other side when

  * setting up the association.

  */

 memset(&initmsg, 0, sizeof(struct sctp_initmsg));

 initmsg.sinit_num_ostreams = MAX_STREAM;

 initmsg.sinit_max_instreams = MAX_STREAM;

 initmsg.sinit_max_attempts = MAX_STREAM;

 ret = setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,

     sizeof(struct sctp_initmsg));

 if (ret < 0) {

  perror("setsockopt SCTP_INITMSG");

  exit(1);

 }



 if (connect(fd, (struct sockaddr *)addr, sizeof (*addr)) == -1) {

  perror("connect");

  exit(1);

 }



 /* Initialize the message header structure for sending. */

 memset(msg, 0, sizeof (*msg));

 iov->iov_base = buf;

 msg->msg_iov = iov;

 msg->msg_iovlen = 1;

 msg->msg_control = cbuf;

 msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);

 msg->msg_flags |= MSG_XPG4_2;



 memset(cbuf, 0, sizeof (*cmsg) + sizeof (*sri));

 cmsg = (struct cmsghdr *)cbuf;

 sri = (struct sctp_sndrcvinfo *)(cmsg + 1);



 cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sri);

 cmsg->cmsg_level = IPPROTO_SCTP;

 cmsg->cmsg_type  = SCTP_SNDRCV;



 sri->sinfo_ppid   = 1;

 /* Start sending to stream 0. */

 sri->sinfo_stream = 0;



 /* Create a thread to receive network traffic. */

 perr = pthread_create(&tid, NULL, (void *(*)(void *))readit, &fd);



 if (perr != 0) {

  fprintf(stderr, "pthread_create: %d\n", perr);

  exit(1);

 }



 /* Read from stdin and then send to the echo server. */

 while ((n = read(fileno(stdin), buf, BUFLEN)) > 0) {

  iov->iov_len = n;

  if (sendmsg(fd, msg, 0) < 0) {

   perror("sendmsg");

   exit(1);

  }

  /* Send the next message to a different stream. */

  sri->sinfo_stream = (sri->sinfo_stream + 1) % MAX_STREAM;

 }



 pthread_cancel(tid);

 close(fd);

}



int

main(int argc, char **argv)

{

 struct sockaddr_in addr[1];

 struct hostent *hp;

 int error;



 if (argc < 2) {

     usage(*argv);

     exit(1);

 }



 /* Find the host to connect to. */ 

 hp = getipnodebyname(argv[1], AF_INET, AI_DEFAULT, &error);

 if (hp == NULL) {

  fprintf(stderr, "host not found\n");

  exit(1);

 }



 addr->sin_family = AF_INET;

 addr->sin_addr.s_addr = *(ipaddr_t *)hp->h_addr_list[0];

 addr->sin_port = htons(5000);



 echo(addr);



 return (0);

}


示例 7–18 SCTP 回显服务器

/*

 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.

 * Use is subject to license terms.

 */



/* To enable socket features used for SCTP socket. */

#define _XPG4_2

#define __EXTENSIONS__



#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <stdlib.h>

#include <unistd.h>

#include <netinet/sctp.h>



#define BUFLEN 1024



/*

 * Given an event notification, print out what it is.

 */

static void

handle_event(void *buf)

{

 struct sctp_assoc_change *sac;

 struct sctp_send_failed  *ssf;

 struct sctp_paddr_change *spc;

 struct sctp_remote_error *sre;

 union sctp_notification  *snp;

 char    addrbuf[INET6_ADDRSTRLEN];

 const char   *ap;

 struct sockaddr_in  *sin;

 struct sockaddr_in6  *sin6;



 snp = buf;



 switch (snp->sn_header.sn_type) {

 case SCTP_ASSOC_CHANGE:

  sac = &snp->sn_assoc_change;

  printf("^^^ assoc_change: state=%hu, error=%hu, instr=%hu "

      "outstr=%hu\n", sac->sac_state, sac->sac_error,

      sac->sac_inbound_streams, sac->sac_outbound_streams);

  break;

 case SCTP_SEND_FAILED:

  ssf = &snp->sn_send_failed;

  printf("^^^ sendfailed: len=%hu err=%d\n", ssf->ssf_length,

      ssf->ssf_error);

  break;

 case SCTP_PEER_ADDR_CHANGE:

  spc = &snp->sn_paddr_change;

  if (spc->spc_aaddr.ss_family == AF_INET) {

   sin = (struct sockaddr_in *)&spc->spc_aaddr;

   ap = inet_ntop(AF_INET, &sin->sin_addr, addrbuf,

       INET6_ADDRSTRLEN);

  } else {

   sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;

   ap = inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf,

       INET6_ADDRSTRLEN);

  }

  printf("^^^ intf_change: %s state=%d, error=%d\n", ap,

      spc->spc_state, spc->spc_error);

  break;

 case SCTP_REMOTE_ERROR:

  sre = &snp->sn_remote_error;

  printf("^^^ remote_error: err=%hu len=%hu\n",

      ntohs(sre->sre_error), ntohs(sre->sre_length));

  break;

 case SCTP_SHUTDOWN_EVENT:

  printf("^^^ shutdown event\n");

  break;

 default:

  printf("unknown type: %hu\n", snp->sn_header.sn_type);

  break;

 }

}



/*

 * Receive a message from the network.

 */

static void *

getmsg(int fd, struct msghdr *msg, void *buf, size_t *buflen,

    ssize_t *nrp, size_t cmsglen)

{

 ssize_t  nr = 0;

 struct iovec iov[1];



 *nrp = 0;

 iov->iov_base = buf;

 msg->msg_iov = iov;

 msg->msg_iovlen = 1;



 /* Loop until a whole message is received. */

 for (;;) {

  msg->msg_flags = MSG_XPG4_2;

  msg->msg_iov->iov_len = *buflen;

  msg->msg_controllen = cmsglen;



  nr += recvmsg(fd, msg, 0);

  if (nr <= 0) {

   /* EOF or error */

   *nrp = nr;

   return (NULL);

  }



  /* Whole message is received, return it. */

  if (msg->msg_flags & MSG_EOR) {

   *nrp = nr;

   return (buf);

  }



  /* Maybe we need a bigger buffer, do realloc(). */

  if (*buflen == nr) {

   buf = realloc(buf, *buflen * 2);

   if (buf == 0) {

    fprintf(stderr, "out of memory\n");

    exit(1);

   }

   *buflen *= 2;

  }



  /* Set the next read offset */

  iov->iov_base = (char *)buf + nr;

  iov->iov_len = *buflen - nr;



 }

}



/*

 * The echo server.

 */

static void

echo(int fd)

{

 ssize_t   nr;

 struct sctp_sndrcvinfo *sri;

 struct msghdr  msg[1];

 struct cmsghdr  *cmsg;

 char   cbuf[sizeof (*cmsg) + sizeof (*sri)];

 char   *buf;

 size_t   buflen;

 struct iovec  iov[1];

 size_t   cmsglen = sizeof (*cmsg) + sizeof (*sri);



 /* Allocate the initial data buffer */

 buflen = BUFLEN;

 if ((buf = malloc(BUFLEN)) == NULL) {

  fprintf(stderr, "out of memory\n");

  exit(1);

 }



 /* Set up the msghdr structure for receiving */

 memset(msg, 0, sizeof (*msg));

 msg->msg_control = cbuf;

 msg->msg_controllen = cmsglen;

 msg->msg_flags = 0;

 cmsg = (struct cmsghdr *)cbuf;

 sri = (struct sctp_sndrcvinfo *)(cmsg + 1);



 /* Wait for something to echo */

 while ((buf = getmsg(fd, msg, buf, &buflen, &nr, cmsglen)) != NULL) {



  /* Intercept notifications here */

  if (msg->msg_flags & MSG_NOTIFICATION) {

   handle_event(buf);

   continue;

  }



  iov->iov_base = buf;

  msg->msg_iov = iov;

  msg->msg_iovlen = 1;

  iov->iov_len = nr;

  msg->msg_control = cbuf;

  msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);



  printf("got %u bytes on stream %hu:\n", nr,

      sri->sinfo_stream);

  write(0, buf, nr);



  /* Echo it back */

  msg->msg_flags = MSG_XPG4_2;

  if (sendmsg(fd, msg, 0) < 0) {

   perror("sendmsg");

   exit(1);

  }

 }



 if (nr < 0) {

  perror("recvmsg");

 }

 close(fd);

}



int

main(void)

{

 int    lfd;

 int    cfd;

 int    onoff = 1;

 struct sockaddr_in  sin[1];

 struct sctp_event_subscribe events;

 struct sctp_initmsg  initmsg;



 if ((lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {

  perror("socket");

  exit(1);

 }



 sin->sin_family = AF_INET;

 sin->sin_port = htons(5000);

 sin->sin_addr.s_addr = INADDR_ANY;

 if (bind(lfd, (struct sockaddr *)sin, sizeof (*sin)) == -1) {

  perror("bind");

  exit(1);

 }



 if (listen(lfd, 1) == -1) {

  perror("listen");

  exit(1);

 }



 (void) memset(&initmsg, 0, sizeof(struct sctp_initmsg));

 initmsg.sinit_num_ostreams = 64;

 initmsg.sinit_max_instreams = 64;

 initmsg.sinit_max_attempts = 64;

 if (setsockopt(lfd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,

     sizeof(struct sctp_initmsg)) < 0) { 

  perror("SCTP_INITMSG");

  exit (1);

 }



 /* Events to be notified for */

 (void) memset(&events, 0, sizeof (events));

 events.sctp_data_io_event = 1;

 events.sctp_association_event = 1;

 events.sctp_send_failure_event = 1;

 events.sctp_address_event = 1;

 events.sctp_peer_error_event = 1;

 events.sctp_shutdown_event = 1;



 /* Wait for new associations */

 for (;;) {

  if ((cfd = accept(lfd, NULL, 0)) == -1) {

   perror("accept");

   exit(1);

  }



  /* Enable ancillary data */

  if (setsockopt(cfd, IPPROTO_SCTP, SCTP_EVENTS, &events,

      sizeof (events)) < 0) {

   perror("setsockopt SCTP_EVENTS");

   exit(1);

  }

  /* Echo back any and all data */

  echo(cfd);

 }

}