现在的位置: 首页 > 综合 > 正文

高性能网络编程,第 1 部分: 最大程度地利用您的网络资源

2013年10月02日 ⁄ 综合 ⁄ 共 3508字 ⁄ 字号 评论关闭
 
如果您具有基于 UNIX® 的编程经验,那么您可能会在一定程度上苦恼于如何提高您的网络吞吐量。本文介绍了一些有价值的技术,使用本文中描述的这些方法,您可以最大程度地利用您的带宽,并实现显著的性能提升。

引言

任何具有 UNIX® 系统编程经验的人都会苦恼于如何提高网络吞吐量以及磁盘 I/O(在某些情况下)。本文介绍了协议实现者所使用的一些高级编程技术,它们可以帮助您充分利用现有的带宽。(本文并不打算介绍如何优化您的操作系统 (OS)、配置内核、或者系统调整。)

尽管特定协议的可用带宽受到 Shannon 定律和其他一些因素(如网络使用模式)的限制,但大多数时候是因为低劣的编程或者未经过实验的编码导致网络资源的使用率无法达到最佳状态。

性能增强不仅仅是一种科学,而且还是一门艺术。要获得最佳的端到端吞吐量,您必须使用各种工具以度量性能、识别瓶颈,并消除它们,或者使得它们的影响最小化。通过一些简单且直观的科学方法,您可以快速地获得显著的性能提升。

流水线和持久连接

流水线 是 CPU 所使用的一个众所周知的概念,它用于减少取指令-译码-执行 周期中出现的延迟。在获取每条指令期间会出现某种延迟,可以通过预取指令并对其进行存储以用于后续的执行,从而避免延迟。可以将相同的概念应用于网络,正如本部分中所描述的。

可以提前通知服务器在处理完当前请求后客户所关心的是哪些内容,而不是使服务器按老一套方式处理来自客户的请求。服务器维护一个挂起请求队列,依次地执行它们,而不是先执行一个请求,然后再读取下一个请求,等等。这样做提高了交互式应用程序的响应,并且与任何其他技术相比,可以更有效地减少延迟。

但是,并不总是能够这样做。即使在它可以起到作用的情况下,有时候服务器队列也可能耗尽。这种情况非常少见,并且大多数时候流水线都可以很好地工作。所有常见的协议,包括 HTTP 1.1、NNTP、X11 等,都通过某种方式使用了流水线。

持久的传输控制协议(Transmission Control Protocol,TCP)连接

如果您打算为每个请求或者事务发起不同的 TCP 连接,那么上述的技术很明显会失效。您应该重用现有的连接,当然这并不是唯一的原因。在建立和拆除连接的过程中,TCP 握手可能会带来巨大的开销,当然最好能够避免这个开销。

如果不能正确地关闭 TCP 连接,那么可能很快会给您的 UNIX 系统带来各种各样的麻烦。另一个因素是新的连接中的协议开销。您希望确保网络尽可能用于实际数据,而不是交换 Header 以及其他控制信息。图 1 显示了流水线和普通处理。

图 1. 流水线和普通处理

如何将这转换为编程实现呢?使用现有的 TCP 连接非常容易,但实现流水线并不是那么简单。协议设计必须考虑跟踪挂起的请求,并确定某个响应对应于哪个请求。

下一个部分将说明能够起到帮助作用的其他机制。

非阻塞的 I/O、select(2) 和 poll(2)

您应该熟悉这两种编程模型:同步和异步处理。本文介绍的流水线技术是使用异步处理以提高性能的示例。同步编程将带来简单的设计、更简单的代码,但有时候性能比较糟糕,这是我们不希望看到的。为了避免这个问题,您必须使用其他的一些技巧,如非阻塞 I/O。

阻塞和非阻塞套接字大致上对应于同步和异步处理,但并不是在网络级别,而是在操作系统级别。在使用阻塞套接字进行典型的 socket write(2) 或者 send(2) 操作时,用户进程必须等待系统调用返回。内核负责将进程转移到睡眠状态、等待套接字做好写入的准备、读取 TCP 状态代码,等等。最后,将控制权返回给应用程序。

在使用非阻塞套接字的情况下,麻烦在于程序员必须确保该套接字是可写入的,并且必须确保正确地写入所有数据。这显然给编程带来了一些不便之处,并且必须了解一种新的习惯用法,但是在掌握了该方法之后,它将成为一种为所有网络代码提供优秀性能的功能强大的工具。

当您的套接字变成非阻塞时,仅使用 read(2)write(2),或 recv(2) 或者 send(2) 是不够的。您必须使用附加的系统调用,如 poll(2) 或者 select(2),以便确定什么时候可以对套接字进行写入,或者从网络进行读取操作。

其中一种选择是使用 poll(2) 来确定可写入性(因为 select(2) 无法完成这一项任务),并使用 select(2) 来确定另一端的数据何时到达您的套接字。清单 1 详细地介绍了一个非阻塞 I/O 的示例。

清单 1. 一个非阻塞 I/O 的示例

1 /******************************************
2 * Non blocking socket read with poll(2) *
3 * *
4 *****************************************/
5
5 void
6 poll_wait(int fd, int events)
7 {
8 int n;
9 struct pollfd pollfds[1];
10 memset((char *) pollfds, 0, sizeof(pollfds));
11
12 pollfds[0].fd = fd;
13 pollfds[0].events = events;
14
15 n = poll(pollfds, 1, -1);
16 if (n < 0) {
17 perror("poll()");
18 errx(1, "Poll failed");
19 }
20 }
21
22 size_t
23 readall(int sock, char *buf, size_t n) {
24 size_t pos = 0;
25 ssize_t res;
26
27 while (n > pos) {
28 res = read (sock, buf + pos, n - pos);
29 switch ((int)res) {
30 case -1:
31 if (errno == EINTR || errno == EAGAIN)
32 continue;
33 return 0;
34 case 0:
35 errno = EPIPE;
36 return pos;
37 default:
38 pos += (size_t)res;
39 }
40 }
41 return (pos);
42 }
43
44 size_t
45 readmore(int sock, char *buf, size_t n) {
46
47 fd_set rfds;
6
48 int ret, bytes;
49
50
51
52 poll_wait(sock,POLLERR | POLLIN );
53 bytes = readall(sock, buf, n);
54
55 if (0 == bytes) {
56 perror("Connection closed");
57 errx(1, "Readmore Connection closure");
58 /* NOT REACHED */
59 }
60
61 return bytes;
62 }
63
64 /******************************************
65 * Non blocking socket write with poll(2) *
66 * *
67 *****************************************/
68
69
70 void
71 poll_wait(int fd, int events)
72 {
73 int n;
74 struct pollfd pollfds[1];
75 memset((char *) &pollfds, 0, sizeof(pollfds));
76
77 pollfds[0].fd = fd;
78 pollfds[0].events = events;
79
80 n = poll(pollfds, 1, -1);
81 if (n < 0) {
82 perror("poll()");
83 errx(1, "Poll problem");
84 }
85 }
86
87
88 size_t
89 writenw(int fd, char *buf, size_t n)
90 {
7
91 size_t pos = 0;
92 ssize_t res;
93 while (n > pos) {
94 poll_wait(fd, POLLOUT | POLLERR);
95 res = write (fd, buf + pos, n - pos);
96 switch ((int)res) {
97 case -1:
98 if (errno == EINTR || errno == EAGAIN)
99 continue;
100 return 0;
101 case 0:
102 errno = EPIPE;
103 return pos;
104 default:
105 pos += (size_t)res;
106 }
107 }
108 return (pos);
109
110 }
111
112

Internet 协议 (IP) 分片以及其他随机的网络影响

本文转自:IBM developerWorks 中国
点击此处查看全文

抱歉!评论已关闭.