SCTP详解

SCTP详解转载自:IBM中文官网sctp部分,代码下载地址:http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=163181&filename=l-sctp-msdemo.zip&method=http&locale=zh_CN作者:M.TimJones是一名嵌入式软件工程师,他是 GNU/LinuxAppl

大家好,又见面了,我是你们的朋友全栈君。

转载自:IBM中文官网sctp部分,代码下载地址:http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=163181&filename=l-sctp-msdemo.zip&method=http&locale=zh_CN

作者:M. Tim Jones 是一名嵌入式软件工程师,他是 GNU/Linux Application ProgrammingAI Application Programming和 BSD Sockets Programming from a Multilanguage Perspective 等书的作者。他的工程背景非常广泛,从同步宇宙飞船的内核开发到嵌入式架构设计,再到网络协议的开发。Tim 是 Emulex Corp. 的一名资深软件工程师。


SCTP 相对于传统的传输层协议来说,两个重要的增强是终端主机的多宿主和多流功能。

多宿主

多宿主 为应用程序提供了比 TCP 更高的可用性。多宿主主机就是一台具有多个网络接口的主机,因此可以通过多个 IP 地址来访问这台主机。在 TCP 中,连接(connection) 是指两个端点之间的一个通道(在这种情况下,就是两台主机的网络接口之间的一个套接字)。SCTP 引入了 联合(association) 的概念,它也是存在于两台主机之间,但可以使用每台主机上的多个接口进行协作。

图 2 阐述了 TCP 连接与 SCTP 联合之间的区别。


图 2. TCP 连接与 SCTP 联合
TCP 连接与 SCTP 联合 

该图的上面部分是 TCP 连接,每个主机都只包含一个网络接口;连接是在每个客户机和服务器之间的单个接口之间建立的。在建立连接时,就被绑定到了每个接口上。

在该图的下面部分中,您可以看到这样一个架构:每台主机上都包含两个网络接口。通过独立网络提供了两条路径,一条是从接口 C0 到 S0,另外一条是从接口 C1 到 S1。在 SCTP 中,这两条路径可以合并到一个联合中。

SCTP 负责使用内嵌的 heartbeat 机制来监视联合的路径;在检测到一条路径失效时,协议就会通过另外一条路径来发送通信数据。应用程序甚至都不必知道发生了故障恢复。

故障转移也可以用于维护网络应用程序的连通性。例如,让我们来考虑一台包含一个无线 802.11 接口和一个以太网接口的笔记本的例子。当笔记本放到固定的位置上时,我们倾向于使用高速的以太网接口(在 SCTP 中称为 主地址(primary address));但是在这个连接丢失时(例如离开了固定位置),连接可迁移到无线接口上。在返回固定位置时,以太网连接会被重新检测到,通信就可以在这个接口上恢复。这是一种能提供更高的可用性和可靠性的强大机制。

多流

从某种意义上来讲,SCTP 连接与 TCP 连接类似,不同之处只是 SCTP 能够在一个联合中支持多流机制。一个联合中的所有流都是独立的,但均与该联合相关(请参见图 3)。



图 3. SCTP 联合与流之间的关系


SCTP 联合与流之间的关系
 

每个流都给定了一个流编号,它被编码到 SCTP 报文中,通过联合在网络上传送。多流非常重要,因为阻塞的流(例如等待重传的流会导致报文的丢失)不会影响同一联合中的其他流。这个问题统称为 head-of-line blocking(对头阻塞)。TCP 很容易出现这类阻塞问题。

多流如何在传输数据时提供更好的响应性呢?例如,HTTP 协议会在相同套接字上共享控制和数据。Web 客户机从服务器上请求一个文件,服务器通过相同的连接将这个文件发回给客户机。多流的 HTTP 服务器可以提供更好的交互能力,因为在联合中各单独的流上可以处理多个请求。这种功能可以并行化响应,尽管速度不一定会更快,但可以同时加载 HTML 和图像映像,从而表现出更好的响应性。

多流处理是 SCTP 的一个重要特性,尤其是在协议的设计中考虑一些控制和数据的问题时更是如此。在 TCP 中,控制和数据通常都是通过相同的连接进行共享的,这可能会产生问题,因为控制报文可能会在数据报之后延时。如果控制和数据被划分成单独的流,控制数据就可以以一种更及时的方式进行处理,从而可以更好地利用可用资源。

初始化保护

TCP 和 SCTP 中对新连接的初始化是通过报文握手来完成的。在 TCP 中,这种机制称为 三次握手(three-way handshake)。客户机向服务器首先发送一个 SYN 报文(Synchronize 的简写),服务器使用一个 SYN-ACK 报文进行响应(Synchronize-Acknowledge)。最后,客户机使用一个 ACK 报文确认已接收到报文(请参见图 4)。



图 4. TCP 和 STCP 握手使用的报文交换


TCP 和 STCP 握手使用的报文交换
 

当恶意客户机使用虚假的源地址来伪造一个 IP 报文时,TCP 就会出现问题了,这会大量 TCP SYN 报文攻击服务器。服务器在接收 SYN 报文之前,要为连接分配资源,但是在大量产生 SYN 报文的情况下,最终会耗尽自己的资源,从而无法处理新的请求。这种情况就称为 服务拒绝(Denial of Service)(DoS)攻击。

SCTP 可以通过一种 4 次握手的机制并引入了 cookie 的概念来有效地防止这种攻击的产生。在 SCTP 中,客户机使用一个INIT 报文发起一个连接。服务器使用一个 INIT-ACK 报文进行响应,其中就包括了 cookie(标识这个连接的惟一上下文)。客户机然后就使用一个 COOKIE-ECHO 报文进行响应,其中包含了服务器所发送的 cookie。现在,服务器要为这个连接分配资源,并通过向客户机发送一个 COOKIE-ACK 报文对其进行响应。

要解决使用这种 4 次握手机制解决延时数据移动的问题,SCTP 允许把数据包含到 COOKIE-ECHO 和 COOKIE-ACK 报文中。

消息分帧

使用消息分帧机制,就可以保护消息只在一个边界内通过 socket 进行通信;这意味着如果客户机向服务器先发送 100 个字节,然后又发送 50 个字节。那么服务器就会在两次读取操作中分别读取到 100 个字节和 50 个字节。UDP 也是这样进行操作,这对于面向消息的协议非常有益。

与此不同,TCP 是按照字节流的方式进行操作。如果没有分帧机制,一端接收到的数据可能比另外一端发送的数据多或少(这会将一次写操作划分成多次操作,或者将多次写操作合并到一个读操作中)。这种行为需要在 TCP 之上进行操作的面向消息的协议可以在应用层中提供数据缓冲和消息分帧机制(这可能是一项复杂的任务)。

SCTP 在数据传输中提供了消息分帧功能。当一端对一个套接字执行写操作时,可确保对等端读出的数据大小与此相同(请参见图 5)。



图 5. UDP/SCTP 中的消息分帧与面向字节流协议的比较


UDP/SCTP 中的消息分帧与面向字节流协议的比较
 

对于面向流的数据来说,例如音频和视频数据,可以没有分帧机制。

可配置的无序发送

SCTP 中的消息的传输十分可靠,但未必是按照想要的次序来传输的。TCP 可以确保数据是按照次序发送的(考虑到 TCP 是一种流协议,这是一件好事)。UDP 无法确保有序地发送数据。但是如果需要,您也可以在 SCTP 中配置流来接受无序的消息。

这种特性在面向消息的协议中可能非常有用,因为其中的消息都是独立的,次序并不重要。另外,您可以在一个联合中按照逐个流配置无序发送。

平滑关闭

TCP 和 SCTP 都是基于连接的协议,而 UDP 则是一种无连接的协议。TCP 和 SCTP 都需要在对等的两端建立和拆除连接。SCTP 与 TCP 中关闭连接的不同之处在于 TCP 中连接的删除是半关闭(half-close) 的。

图 6 给出了 TCP 和 SCTP 的关闭序列。



图 6. TCP 和 SCTP 的连接结束序列


TCP 和 SCTP 的连接结束序列
 

在 TCP 中,一端可以关闭自己这端的 socket(这样会导致发送一个 FIN 报文),但是仍然可以继续接收数据。FIN 说明这个端点不会再发送数据,但是在这一端关闭自己这端的套接字之前,它一直可以继续传输数据。应用程序很少使用这种半关闭状态,因此 SCTP 的设计者就选择放弃这种状态,并将其替换成了一个显式的终结序列。当一端关闭自己的套接字时(导致产生一个 SHUTDOWN 原语),对等的两端全部需要关闭,将来任何一端都不允许再进行数据的移动了。

多流的展示

现在您已经了解了 SCTP 的基本特性了,接下来让我们来看一下使用 C 编程语言编写的一个样例服务器和客户机,并展示 SCTP 的多流特性。

这个例子开发了一个服务器,它实现了一种形式的日期查询协议。这个传统的服务器会在连接上来的客户机上打印当前时间,但是对于 SCTP 来说,我们会在流 0 上打印本地时间,在流 1 上打印格林威治时间(GMT)。这个例子让我们可以展示如何使用这些 API 来开发流通信。

图 7 对整个过程进行了归纳,它不但从套接字 API 的角度展示了应用程序的流程,而且还从客户机和服务器的角度介绍了它们之间的关系。

图 7. 在多流日期查询服务器和客户机中使用的套接字函数


在多流日期查询服务器和客户机中使用的套接字函数
 

这些应用程序是在 GNU/Linux 操作系统上开发的,其内核版本是 2.6.11,并且包含了 Linux Kernel SCTP 项目(lksctp)。其中非标准的 socket 函数是在 lksctp 工具包中提供的,这个工具包可以从 SourceForge 上获得。请参看 参考资料 中的链接。

daytime 服务器

清单 1 给出了这个多流 daytime 服务器的代码。为了可读性更好,我们在清单 1 中忽略了所有的错误检查,但是 这些展示错误检查机制的代码 与其他 SCTP 套接字扩展一样都可以通过给出的链接下载到。



清单 1. 使用多流机制为 SCTP 编写的日期查询服务器

int main()
{
  int listenSock, connSock, ret;
  struct sockaddr_in servaddr;
  char buffer[MAX_BUFFER+1];
  time_t currentTime;
  /* Create SCTP TCP-Style Socket */
  listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
  /* Accept connections from any interface */
  bzero( (void *)&servaddr, sizeof(servaddr) );
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
  servaddr.sin_port = htons(MY_PORT_NUM);
  /* Bind to the wildcard address (all) and MY_PORT_NUM */
  ret = bind( listenSock,
               (struct sockaddr *)&servaddr, sizeof(servaddr) );
  /* Place the server socket into the listening state */
  listen( listenSock, 5 );
  /* Server loop... */
  while( 1 ) {
    /* Await a new client connection */
    connSock = accept( listenSock,
                        (struct sockaddr *)NULL, (int *)NULL );
    /* New client socket has connected */
    /* Grab the current time */
    currentTime = time(NULL);
    /* Send local time on stream 0 (local time stream) */
    snprintf( buffer, MAX_BUFFER, "%s\n", ctime(&currentTime) );
    ret = sctp_sendmsg( connSock,
                          (void *)buffer, (size_t)strlen(buffer),
                          NULL, 0, 0, 0, LOCALTIME_STREAM, 0, 0 );
    /* Send GMT on stream 1 (GMT stream) */
    snprintf( buffer, MAX_BUFFER, "%s\n",
               asctime( gmtime( &currentTime ) ) );
    ret = sctp_sendmsg( connSock,
                          (void *)buffer, (size_t)strlen(buffer),
                          NULL, 0, 0, 0, GMT_STREAM, 0, 0 );
    /* Close the client connection */
    close( connSock );
  }
  return 0;
}


清单 1 中的服务器首先创建服务器的套接字(使用 IPPROTO_SCTP 来创建一个 SCTP 的一对一的套接字)。然后创建一个sockaddr 结构,指定这个连接可以从任何本地接口上创建(使用通配符地址 INADDR_ANY)。我们使用 bind 调用将这个sockaddr 结构绑定到 socket 上,然后将服务器套接字设置成监听状态。现在就可以接收到达的连接了。

注意 SCTP 使用了很多与 TCP 和 UDP 相同的套接字 API。在 lksctp 开发工具中还提供了其他一些 API 函数(请参看 参考资料)。

在服务器的循环中,一直等待新客户机的连接请求。在从 accept 函数返回时,会使用 connSock socket 标识新客户机的连接。我们使用 time 函数来获取当前时间,然后使用 snprintf 将其转换成字符串。使用 sctp_sendmsg 函数(一个非标准的 socket 调用),可以通过指定特定的流程(LOCALTIME_STREAM,将这个字符串发送给客户机。当发送本地时间字符串之后,我们将使用 GMT 表示的当前时间转换成一个字符串,然后将其发送到流 GMT_STREAM 上。

现在,日期查询服务器已经完成了自己的职责,因此我们就可以关闭这个 socket,然后等待一个新的客户机连接。一切都非常简单,对吗?现在让我们来看一下日期查询客户机是如何处理多流的。

日期查询客户机

多流客户机如清单 2 所示。



清单 2. 使用多流机制为 SCTP 编写的日期查询客户机

int main()
{
  int connSock, in, i, flags;
  struct sockaddr_in servaddr;
  struct sctp_sndrcvinfo sndrcvinfo;
  struct sctp_event_subscribe events;
  char buffer[MAX_BUFFER+1];
  /* Create an SCTP TCP-Style Socket */
  connSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
  /* Specify the peer endpoint to which we'll connect */
  bzero( (void *)&servaddr, sizeof(servaddr) );
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(MY_PORT_NUM);
  servaddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
  /* Connect to the server */
  connect( connSock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
  /* Enable receipt of SCTP Snd/Rcv Data via sctp_recvmsg */
  memset( (void *)&events, 0, sizeof(events) );
  events.sctp_data_io_event = 1;
  setsockopt( connSock, SOL_SCTP, SCTP_EVENTS,
               (const void *)&events, sizeof(events) );
  /* Expect two messages from the peer */
  for (i = 0 ; i < 2 ; i++) {
    in = sctp_recvmsg( connSock, (void *)buffer, sizeof(buffer),
                        (struct sockaddr *)NULL, 0,
                        &sndrcvinfo, &flags );
    /* Null terminate the incoming string */
    buffer[in] = 0;
    if        (sndrcvinfo.sinfo_stream == LOCALTIME_STREAM) {
      printf("(Local) %s\n", buffer);
    } else if (sndrcvinfo.sinfo_stream == GMT_STREAM) {
      printf("(GMT  ) %s\n", buffer);
    }
  }
  /* Close our socket and exit */
  close(connSock);
  return 0;
}


在客户机中,我们首先创建了一个 SCTP 套接字,然后创建了一个 sockaddr 结构,其中包含了将要连接的端点。connect 函数然后建立一个到服务器的连接。要获取消息的流编号,SCTP 需要启用套接字选项 sctp_data_io_event

通过启用这个选项,我们就可以通过 sctp_recvmsg API 函数接收一条消息,我们还接收到一个包含流编号的sctp_sndrcvinfo 结构。这个编号让我们可以区分开流 0(本地时间)和流 1(GMT)的消息。

SCTP 的未来发展

SCTP 是一个相当新的协议,它在 2000 年 10 月份才成为一个 RFC 规范。从那以后,它开始进入所有的主流操作系统,包括 GNU/Linux、BSD 和 Solaris。在 Microsoft? Windows? 操作系统上也有第三方的商业包可以使用。

在获得高可用性的同时,应用程序也已经开始使用 SCTP 作为自己的主要传输机制。诸如 FTP 和 HTTP 之类的传统应用程序已经在 SCTP 的特性基础上进行了构建。其他一些协议也正在开始使用 SCTP,例如会话初始化协议(Session Initiation Protocol,SIP)和通用通道信号系统 7(SS7)。在商业领域中,您可以在 Cisco 的 IOS 中找到 SCTP 的影子。

随着 SCTP 被吸纳到 2.6 版本的 Linux 内核中,现在我们可以构建并部署高可用性、高可靠性的网络应用程序。作为一种基于 IP 的协议,SCTP 不但可以无缝地替换 TCP 和 UDP,而且扩展了很多新服务,例如多宿主、多流,并且对安全性也有了很大的提高。现在您已经了解了 SCTP 的一些高级特性,并且探索了它的一些其他功能。Linux Kernel SCTP 项目(lksctp)提供了可以为您提供辅助的 API 扩展和文档。


参考资料

学习

获得产品和技术

讨论

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148169.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 微信火柴人html5小游戏,20个好玩的微信小游戏推荐!你玩过几个?「建议收藏」

    50000+游戏爱好者已加入我们!每天推荐好玩游戏!加入我们,沐沐带你发现好游戏!只有你想不到,没有我找不到的好游戏!「良心好游戏推荐」搜罗了好玩的微信小游戏大全,模拟经营游戏、恐怖游戏、消除游戏、休闲游戏、益智游戏、吃鸡游戏、烧脑游戏、解谜游戏大全、换装游戏、射击游戏、吃鸡小游戏、像素游戏一个都不少!还有游戏攻略哦!每天都会推荐好玩的小游戏。————————————————————————不知不…

  • 最小二乘法进行线性回归_最小二乘法简单例题

    最小二乘法进行线性回归_最小二乘法简单例题最小二乘法概述对于一元线性回归模型,假设从总体中获取了n组观察值(x1,y1)(x1,y1)(x_1,y_1),(x2,y2)(x2,y2)(x_2,y_2),…,(xn,yn)(xn,yn)(x_n,y_n)。对于平面中的这n个点,可以使用无数条曲线来拟合。要求样本回归函数尽可能好地拟合这组值。综合起来看,这条直线处于样本数据的中心位置最合理。选择最佳拟合曲线的标准可以确定为:使总的拟…

  • intellij idea的快速配置详细使用

    intellij idea的快速配置详细使用IDEA实用教程一、IDEA简介1.简介IDEA全称IntelliJIDEA,是java语言开发的集成环境。IDEA是JetBrains公司的产品。JetBrains官网:https://www.jetbrains.com/IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查方面。了…

  • 控制Tello无人机扫描条形码「建议收藏」

    控制Tello无人机扫描条形码「建议收藏」一直想玩无人机,之前租了一个大疆的发现禁飞。好在最近发现了Tello,买来过了一把瘾。顺便试了下集成条形码扫描功能。现在有很多仓储管理会用到无人机来扫码做库存盘点。Python3控制Tello无人机DJI的官方GitHub仓库里已经放了示例代码dji-sdk/Tello-Python。不过这份代码只能支持Python2.7,而且也好久无人维护。要在Python3上运行这份代码需要做些修改。首先获取源码:gitclonehttps://github.com/dji-sdk/Tello-Py

  • MYSQL中TINYINT的取值范围

    原文地址:https://blog.csdn.net/lysygyy/article/details/5983433在MySQL的数据类型中,Tinyint的取值范围是:带符号的范围是-128到127。无符号的范围是0到255(见官方《MySQL5.1参考手册》http://dev.mysql.com/doc/refman/5.1/zh/column-types.html#numeri…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号