初始化保护TCP 和 SCTP 中对新连接的初始化是通过报文握手来完成的。在 TCP 中,这种机制称为 三次握手(three-way handshake)。客户机向服务器首先发送一个 SYN 报文(Synchronize 的简写),服务器使用一个 SYN-ACK 报文进行响应(Synchronize-Acknowledge)。最后,客户机使用一个 ACK 报文确认已接收到报文(请参见图 4)。
图 4. 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 中的消息分帧与面向字节流协议的比较对于面向流的数据来说,例如音频和视频数据,可以没有分帧机制。
可配置的无序发送SCTP 中的消息的传输十分可靠,但未必是按照想要的次序来传输的。TCP 可以确保数据是按照次序发送的(考虑到 TCP 是一种流协议,这是一件好事)。UDP 无法确保有序地发送数据。但是如果需要,您也可以在 SCTP 中配置流来接受无序的消息。
这种特性在面向消息的协议中可能非常有用,因为其中的消息都是独立的,次序并不重要。另外,您可以在一个联合中按照逐个流配置无序发送。
平滑关闭TCP 和 SCTP 都是基于连接的协议,而 UDP 则是一种无连接的协议。TCP 和 SCTP 都需要在对等的两端建立和拆除连接。SCTP 与 TCP 中关闭连接的不同之处在于 TCP 中连接的删除是半关闭(half-close) 的。
图 6 给出了 TCP 和 SCTP 的关闭序列。
图 6. 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 上获得。请参看 中的链接。 |