使用 SCTP 优化网络

流控制传输协议(Stream Control Transmission Protocol,SCTP)是一种可靠的传输协议,它在两个端点之间提供稳定、有序的数据传递服务(非常类似于 TCP),并且可以保护数据消息边界(例如 UDP)。然而,与 TCP 和 UDP 不同,SCTP 是通过多宿主(Multi-homing)和多流(Multi-streaming)功能提供这些收益的,这两种功能均可提高可用性。在本文中,我们将学习 Linux® 2.6 内核中 SCTP 的关键特性,并通过服务器和客户机的源代码了解该协议提供多流的能力。

SCTP 是在 IP 网络上使用的一种可靠的通用传输层协议。尽管 SCTP 协议最初是为发送电话信号而设计的(RFC 2960),但带来了一个意外的收获:它通过借鉴 UDP 的优点解决了 TCP 的某些局限。SCTP 提供的特性使套接字初始化的可用性、可靠性和安全性都得以提高。(图 1 给出了 IP 堆栈的层次化架构。)

图 1. IP 栈的层次化架构

本文简要介绍了 Linux 2.6 内核中 SCTP 的概念,重点介绍了一些高级特性(例如多宿主和多流),并且给出了服务器和客户机的部分代码片断(并给出了一个可以获得更多代码的 URL),从而展示了这种协议提供多流的能力。

下面让我们开始介绍 IP 堆栈的内容。

IP 堆栈

Internet 协议套件被划分成几层;每层都提供特定功能,如图 1 所示。

自下而上:

  • 链路层(link layer) 提供了通信介质的物理接口(例如以太网设备)。
  • 网络层(network layer) 负责管理网络中的报文移动,具体来说就是确保报文都到达自己的目标(也称为路由)。
  • 传输层(transport layer) 为应用层控制了报文在两台主机之间的流动。它还代表通信的应用程序端点,称为 端口(port)。
  • 最后,应用层(application layer) 对通过套接字传递数据具有深刻的意义。这些数据可能包括通过简单邮件传输协议(Simple Mail Transport Protocol,SMTP)发送的 e-mail 消息,或通过超文本传输协议(Hypertext Transport Protocol,HTTP)呈现的 Web 页面。

所有应用层协议都使用套接字层作为与传输层协议之间的接口。Sockets API 是由 UC Berkeley 在 BSD UNIX® 操作系统上开发的。

在深入钻研 SCTP 之前,让我们首先简单回顾一下传统的传输层协议。

传输层协议

两种最流行的传输层协议是传输控制协议(TCP)和用户数据报协议(UDP):

  • TCP 是一种可靠的协议,它可以确保有序地发送数据,并管理网络中的拥塞问题。
  • UDP 是一种面向消息的协议,它不能确保有序地发送数据,也无法管理网络拥塞的问题。

然而,UDP 是一种快速协议,可以保护自己传输的消息的边界。

本文引出了另外一个选择:SCTP。它提供了像 TCP 一样可靠、有序地发送数据的功能,但却以像 UDP 一样面向消息的方式来进行操作,这可以保护消息边界。SCTP 还提供了几个高级特性:

  • 多宿主(Multi-homing)
  • 多流(Multi-streaming)
  • 初始化保护(Initiation protection)
  • 消息分帧(Message framing)
  • 可配置的无序发送(Configurable unordered delivery)
  • 平滑关闭(Graceful shutdown)

SCTP 的关键特性

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

多宿主

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

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

图 2. TCP 连接与 SCTP 联合

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

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

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

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

多流

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

图 3. 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 握手使用的报文交换

当恶意客户机使用虚假的源地址来伪造一个 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 上获得。请参看 参考资料 中的链接。

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(¤tTime) );
    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( ¤tTime ) ) );
    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 扩展和文档。


相对于 2.4 版本,新的 Linux 2.6 内核具有很多改进。内核网络选项是技术进步的一个方面。尽管与网络选项相关的大部分文件都有所改善,但本文只是关注影响整个系统的主要特性的改进和增加,而不是关注特定的文件。

明确地说,在本文中我们将介绍网络文件系统 (Networking File System, NFS) 和 Internet 协议安全 (Internet Protocol Security, IPSec) 的改进。我们还将接触 TCP/IP 协议簇的两个新成员,流控制传输协议 (Stream Control Transmission Protocol, SCTP) 和 Internet 协议版本 6 (Internet Protocol version 6, IPv6)。

网络文件系统和安全

2.6 内核通过引入 NFS 版本 4 而改进了网络文件系统 (NFS)。这个新版本的 NFS 有更好的安全性,考虑了更多的跨不同操作系统的支持,并且减轻了服务器后台进程开销。

2.6 内核对网络文件系统版本 4 (NFSv4) 的引入,带来了 NFS 先前版本不曾出现过的安全性和功能性的改进。使用通用安全服务 (General Security Service, GSS) API 的远程过程调用 (remote procedure call, RPC) 实现,NFS 的用户现在可以进行安全的事务处理。设计者还引入了复合过程(将多个 RPC 组合到一个调用中)的思想。调用的组合意味着文件系统操作需要更少的 RPC,从而使 NFS 响应速度更快。

进一步减少 NFS 开销的是,NFS 现在使用了文件“句柄到路径”名称映射 (mountd),以及字节范围 (byte range) 的文件锁定 (lockd),这样减少了服务器端所需要的支撑后台进程的数量。为方便服务器实现,NFSv4 引入了额外的文件句柄类型,并提供了对文件和文件系统属性的分类。这个新的 NFS 版本还增加了对服务器迁移和复制的支持,让用户在需要的时候可以无缝地改变服务器。最后,NFSv4 现在可以让服务器授权给在缓存状态下的客户机某些职责,在那种情况下非常需要这个选项。

NFS RPC 请求可以使用密码认证的能力提供了端到端的 NFS 安全支持。NFSv4 使用 RPCSEC_GSS 框架来扩展 RPC 的基本安全。这个安全框架让 NFSv4 可以提供服务器与客户机之间的认证、完整性和隐私机制。这种联合的安全协商让客户机可以安全地与服务器的安全策略相匹配,得到满足服务器和客户机双方需求的机制。

复合过程是 NFS 在版本 4 设计中的另一个改进。先前版本的 NFS 没有方法可以让客户机生成复杂逻辑的文件系统 RPC。通过使用复合过程,客户机可以将 LOOKUP 、 OPEN 和 READ 操作组合到一个 RPC 请求中,这样客户机只需一次请求就可以从文件中读取数据。旧版本的 NFS 要求客户机为这三个操作每一个都要执行一次 RPC。在服务器端处理这些复合请求的实现很简单,复合请求被服务器拆分为一个单独请求的列表,服务器遍历并执行列表中的每一个操作,直到结束或者失败,然后将所有操作的结果返回给客户机。

NFSv4 通过削减服务器所需要的非 NFS 服务器协议的数目来进行进一步的简化。用版本 4,NFS 代码可以将文件句柄映射到路径名,在旧版本中这是由 mountd 协议来做的。服务器提供了一个 root 文件句柄,对应于服务器导出的文件系统树的顶。服务器支持多文件系统,实现途径是用伪文件系统将它们联系在一起,这样就掩盖了真正的文件系统之间路径名的潜在差异。这个转换是为了支持全局分级名称空间。

此外,这个新版本的 NFS 协议支持字节范围的文件锁定,而先前版本使用的是网络锁管理器 (Network Lock Manager) 提供的 lockd 协议。文件锁定支持的重构让服务器可以用基于租约的模型来保持文件的锁状态。基本来说,客户机需要向服务器提交锁请求。如果被准许,客户机还必须在服务器指定的租约期限内更新租约。在租约过期后服务器可以释放客户机的锁。mountd 和 lockd 这两个协议的弃用,减少了运行 NFS 服务器的处理开销。

新版本的 NFS 还包括简化 NFS 服务器实现的改进。文件句柄要在它所引用的文件系统对象的生命周期内持久保持,这对一些老的 NFS 服务器实现来说是一个很难满足的要求。NFSv4 中增加了一个可变文件句柄类型,作为持久文件句柄类型的补充。使用这两种文件句柄类型,服务器实现可以具备与服务器上操作系统的文件系统相匹敌的能力。客户机可以识别出服务器提供的文件句柄的类型并为其做好准备,然后为每一个句柄设置操作。

文件和文件系统属性分类是对 NFS 的另一个补充,使服务器实现更方便。旧的 NFS 版本使用的是固定的一组属性,只是主要考虑了 UNIX 文件和文件系统。如果服务器或者客户机不支持特定的属性,它必须得尽量去模拟那个属性。版本 4 将属性分为三类:强制的、推荐的和命名的。

强制的属性是服务器必须正确提供并描述的文件或文件系统属性的最小集合。 推荐的属性 描述了不同的文件系统类型和操作系统,考虑到了操作系统之间更好的包含和互操作性。 命名文件系统属性分类是一个关联到目录或者文件的字节流,通过一个字符串名引用。客户机应用程序可以利用这些命名的属性将特定的数据关联到文件 和/或 文件系统。属性分类系统的创建,建立了一个不用对代码进行大的修订就可以添加新属性的简便方法。

为实现更好的冗余,NFSv4 支持服务器端的文件系统复制和迁移。使用一个特殊的文件系统位置属性,客户机可以向服务器进行关于文件系统位置的查询。如果出于负载平衡或者其他类似原因服务器文件系统被复制了,客户机可以得到请求的文件系统的所有位置。客户机运用自己的策略可以挂载并访问它请求的文件系统的适当位置。类似的,如果一个文件系统被迁移了,客户机根据访问旧位置时得到的错误查询文件系统的新位置,并为适应重新定位进行必要的改变。

NFSv4 的最后一个亮点是让服务器可以授权给在缓存状态下的客户机某些职责,这些于提供真正的数据完整性来说是必需的。使用 NFSv4,服务器可以为一个特定的文件提供读或写授权。如果一个客户机得到了一个文件的读授权,那么在这个授权期间,任何其它客户机都不允许写这个文件。此外,如果一个客户机得到了一个文件的写授权,那么在此授权期间没有其他客户机可以写或读这个文件。当一个客户机请求一个文件,而这个文件已授权给另一个客户机时,会发生冲突,授权可能会被服务器收回。在这种情况下,服务器会通过客户机与服务器之间的一个回调路径通知授权的用户,并收回授权。授权让客户机使用 NFS 缓存在本地进行服务操作,而不用与服务器进行实时交互,这样就减少了服务器负载和网络传输。

TCP 改进

流控制传输协议 (SCTP) 是 2.6 内核中增加的一个新的传输层协议。SCTP 除了具有传输控制协议 (TCP) 所具有的相同特性之外,还具备了用于电话、数据通信和高可用应用程序的另外的特性。

SCTP 提供了与 TCP 类似的功能,可以确保无错的、序列化的数据传输,并在数据传输整个过程中在两个端点之间建立一个面向会话的、端到端的联系。不过,SCTP 还提供了 TCP 所不具备的功能,比如 multi-streaming 和 multi-homing,这对某些任务来说至关重要,例如 IP 网络之上的电话信号。

Multi-streaming 让数据可以分为多个独立的序列流。这样的结果是,任何一个流中的消息丢失最初只会影响这个流本身,而不会影响其他的流。SCTP 是面向消息的(相对而言,TCP 是面向字节的),支持构造独立的消息边界,支持多路数据流。如果使用 TCP 中用到的单一数据流方法,当消息丢失或者发生序列错误时,会导致更多的延迟。直到正确的序列被重新存贮之前,TCP 必须延迟向应用程序层的传输。这一数据传输的延迟会影响那些不是必须要求消息排序的应用程序的性能,比如电话信号或者有多媒体内容的网页。虽然电话信号需要对同源的(比如同一次通话)消息进行排序,但是其他相关的消息的传输并不要求序列的完整性。

包含有不同类型和大小的多媒体对象的网页,可以使用 multi-streaming 以部分有序的方法来传输这些内容,而不要用严格有序的方法。这一数据传输的灵活性会提高传输的用户可感知性。此外,在单一的 SCTP 联系中进行数据传输的思想意味着所有的流可以依赖于一个共同的流量和拥塞控制机制,这样减少了传输层所需要的工作。

Multi-homing 是使 SCTP 与传统的传输层协议不同的另一个特性。Multi-homing 允许单一的 SCTP 端点支持多个 IP 地址,并在到目标有多个路由的情况下提供冗余。TCP 和 UDP 用的是 single-homed 会话,这样当本地 LAN 访问失败会隔离开终端系统,并且整个网络内的故障会导致晢时的失败,直到 IP 路由协议重新路由传输。

Multi-homed SCTP 和冗余的 LAN 一道,考虑到了对本地端点访问的增强。多个不同前缀的地址和/或路由,再加上 SCTP multi-homing,提高了贯穿于整个网络的冗余。SCTP 的 multi-homing 特性并不提供网络负载平衡与共享功能。这个机制的关键目的是为 SCTP 之上的应用程序提供冗余的连接。SCTP 指定一个地址作为“主”地址,并使用这个地址来进行所有的数据通信。当需要重传时,数据会发送到所有的地址,以增加到达另一个端点的可能性。在主连接完全失败的情况下,所有的数据被重新路由到另一个地址。与标准高可用性中用到的方法类似,一个“心跳”信号会发送到失败的主连接,这可以用来确定原来的连接是否可以重新建立起来。

IP 安全和压缩

Internet 协议安全 (IPSec) 是对 2.6 内核的另一个增强。IPSec 提供了在局域网和 Internet 上认证和加密网络通信的方法。除了提供包加密外,2.6 内核还通过 IP 有效载荷压缩 (IP Payload Compression, IPComp) 提供了改进的传输。IPComp 是一个协议,它使用压缩和解压缩算法来改进在慢的和/或拥塞的网络上的传输质量。

2.6 内核对 Internet 协议安全 (IPSec) 的引入,在 Internet 协议 (IP) 层向用户提供了安全的传输服务。IPSec 为复杂结合在一起构成 Internet 的媒介和各种应用程序提供了一个通用的解决方案。2.6 内核支持两种 IPSec 机制:认证报头 (Authentication Header, AH) 和封装的安全有效载荷 (Encapsulated Security Payload, ESP)。它们都依赖于 2.6 内核中包含的 Cryptographic API 提供的认证算法。

认证报头 (AH) 是直接添加到 IP 报头之后的附加报头,以提供包认证。包级别的认证让用户可以确保收到的包来自于特定的机器,并且它的内容在传输路径上没有被改变。这种机制没有试图隐藏或保护包的内容。AH 所提供的主要特性是包的完整性保证。要更好地利用加密技术,用户还应该再使用 ESP。

封装的安全有效载荷 (ESP) 报头有能力提供加密和包认证功能。ESP 提供的功能包括加密、认证、“反重放 (anti-replay) 服务(部分序列完整性的一种形式)”和“有限的传输流机密”。用户可以选择不用特定的认证进行加密,但是这样会让包容易受到攻击,导致让别人破坏加密。ESP 报头位于 IP 报头之后,传输模式(UDP 或者 TCP)之前,或者当使用隧道时是在封装的 IP 报头之前。

ESP 保护整个内部的 IP 包和报头。在隧道模式,内部的 IP 报头承载预设的源和最初的目的地址,外部 IP 报头包含用于跳点的 IP 地址,比如安全网关。

IP 有效载荷压缩

IP 有效载荷压缩 (IPComp) 减小了 IP 数据报的大小。如果两端机器都有足够的计算能力,并且通信发生于拥塞的和/或慢的连接上,这一 2.6 网络特性将提高两个端点间通信的性能。

IPComp 协议特别适合于 IPSce 配合使用,因为当使用 IPSec 提供的和需要的附加报头时,包的大小会增加。IPComp 有两个阶段:压缩向外发送的包和解压缩收到的包。在压缩和解压缩的过程中保持了原始 IP 包的数据完整性。每个包的压缩和解压缩都是独立进行的,因为 Internet 内存的不确定性会导致包的到达顺序被打乱。

IPv6 隐私扩展

2.6 内核特性改进了关于 IPv6 的安全选项。除了扩展 IPSec、IPComp 和对 IPv6 隧道的支持外,2.6 内核还提供了 IPv6 隐私扩展。

IPSec 为 IPv6 提供了与 IPv4 的相同级别的认证和安全。所包含的对 IPv6 到 IPv6 隧道的支持让两个端点之间可以进行安全无缝的通信,比如虚拟专用网络 (Virtual Private Networks, VPN) 之上的传输。

IPv6 隐私扩展这一特性尤其着重于提高 Internet 的匿名性,让用户在使用 IPv6 地址时可以选择保护他们的身份。当前的无国界的地址自动配置模式是使用设备(换句话说,是以太网卡或者移动电话)的 MAC 地址来形式化 128 位 IPv6 地址的前缀。使用不变的标识符来形式化地址使得数据可以被追踪,而这可能被意想不到的动机所利用。例如,只要知道一台机器的 MAC 地址,网络嗅探器就可以追踪哪些机器以及在什么时候哪台机器与此机器通信。

网络嗅探器的数据很容易收集,因为无论网络拓扑如何,MAC 地址始终不变,即使机器是移动电话或者膝上电脑。记录这些数据的人可以通过这些信息来追踪工作模式、位置,等等。

IPv6 隐私扩展让用户可以使用一个随机的接口标识符创建另外的 IPv6 全局地址。一台机器在特定的时间段内使用这些临时的地址,直到重新设置为另一个随机地址。重新设置后,当前的连接可以继续保持通信;不过,所有的新连接必须用新的临时地址建立通信。

结束语

大部分用户将发现,这些新的或者增强的特性中,会有一个或多个可以改进他们在各自的系统环境中使用 Linux 的方式。

NFS 用户迁移至版本 4 就可以得到期待中的提高的性能与安全。电信等级 (carier-grade) 和电话应用程序的开发人员可以使用 SCTP 提供的特性来帮助他们确保为消费者和客户提供更好的、更可靠的服务。IPSec 为那些需要通过不安全的网络传输安全数据的人和企业提供了解决方案,IPComp 通过在传输中使用较小的包而让那些人和企业可以改善 Internet 上的数据通信。对 IPv6 的增强可以为那些使用这个下一代 Ineternet 协议的人提供更好的安全性和隐私性,同时让更多 IPv4 应用程序开发人员转而使用这个改进版本的 IP。

总之,2.6 Linux 内核网络方面的增强,是向企业环境中大规模采用 Linux 的方向迈出的积极的一步



你可能感兴趣的:(笔试面试,网络编程)