rfc3117.txt
来自「RFC 的详细文档!」· 文本 代码 · 共 1,516 行 · 第 1/4 页
TXT
1,516 行
the fact that a lack of asynchrony in application protocols causes
implementors to make sloppy use of the transport layer, network
protocols are now provisioned with increasing sophistication, e.g.,
RED [27]. Further, suggestions are also being considered for
modification of TCP implementations to reduce concurrent learning,
e.g., [28].
In terms of the server, each incoming connection must be dispatched
and (probably) authenticated against the same resources.
Consequently, server overhead increases based on the number of
connections established, rather than the number of remote users. The
same issues of fairness arise: it's much harder for servers to
allocate resources on a per-user basis, when a user can cause an
arbitrary number of connections to pound on the server.
Another important aspect of scalability to consider is the relative
numbers of clients and servers. (This is true even in the peer-to-
peer model, where a peer can act both in the client and server role.)
Typically, there are many more client peers than server peers. In
this case, functional requirements should be shifted from the servers
onto the clients. The reason is that a server is likely to be
interacting with multiple clients and this functional shift makes it
easier to scale.
Rose Informational [Page 14]
RFC 3117 On the Design of Application Protocols November 2001
4.2 Efficiency
A well-designed protocol is efficient.
For example, although a compelling argument can be made than octet-
stuffing leads to more elegant implementations than octet-counting,
experience shows that octet-counting consumes far fewer cycles.
Regrettably, we sometimes have to compromise efficiency in order to
satisfy other properties. For example, 822 (and MIME) use textual
headers. We could certainly define a more efficient representation
for the headers if we were willing to limit the header names and
values that could be used. In this case, extensibility is viewed as
more important than efficiency. Of course, if we were designing a
network protocol instead of an application protocol, then we'd make
the trade-offs using a razor with a different edge.
4.3 Simplicity
A well-designed protocol is simple.
Here's a good rule of thumb: a poorly-designed application protocol
is one in which it is equally as "challenging" to do something basic
as it is to do something complex. Easy things should be easy to do
and hard things should be harder to do. The reason is simple: the
pain should be proportional to the gain.
Another rule of thumb is that if an application protocol has two ways
of doing the exact same thing, then there's a problem somewhere in
the architecture underlying the design of the application protocol.
Hopefully, simple doesn't mean simple-minded: something that's well-
designed accommodates everything in the problem domain, even the
troublesome things at the edges. What makes the design simple is
that it does this in a consistent fashion. Typically, this leads to
an elegant design.
4.4 Extensibility
A well-designed protocol is extensible.
As clever as application protocol designers are, there are likely to
be unforeseen problems that the application protocol will be asked to
solve. So, it's important to provide the hooks that can be used to
add functionality or customize behavior. This means that the
protocol is evolutionary, and there must be a way for implementations
reflecting different steps in the evolutionary path to negotiate
which extensions will be used.
Rose Informational [Page 15]
RFC 3117 On the Design of Application Protocols November 2001
But, it's important to avoid falling into the extensibility trap: the
hooks provided should not be targeted at half-baked future
requirements. Above all, the hooks should be simple.
Of course good design goes a long way towards minimizing the need for
extensibility. For example, although SMTP initially didn't have an
extension framework, it was only after ten years of experience that
its excellent design was altered. In contrast, a poorly-designed
protocol such as Telnet [29] can't function without being built
around the notion of extensions.
4.5 Robustness
A well-designed protocol is robust.
Robustness and efficiency are often at odds. For example, although
defaults are useful to reduce packet sizes and processing time, they
tend to encourage implementation errors.
Counter-intuitively, Postel's robustness principle ("be conservative
in what you send, liberal in what you accept") often leads to
deployment problems. Why? When a new implementation is initially
fielded, it is likely that it will encounter only a subset of
existing implementations. If those implementations follow the
robustness principle, then errors in the new implementation will
likely go undetected. The new implementation then sees some, but not
widespread deployment. This process repeats for several new
implementations. Eventually, the not-quite-correct implementations
run into other implementations that are less liberal than the initial
set of implementations. The reader should be able to figure out what
happens next.
Accordingly, explicit consistency checks in a protocol are very
useful, even if they impose implementation overhead.
Rose Informational [Page 16]
RFC 3117 On the Design of Application Protocols November 2001
5. The BXXP Framework
Finally, we get to the money shot: here's what we did.
We defined an application protocol framework called BXXP (the Blocks
eXtensible eXchange Protocol). The reason it's a "framework" instead
of an application protocol is that we provide all the mechanisms
discussed earlier without actually specifying the kind of messages
that get exchanged. So, when someone else needs an application
protocol that requires connection-oriented, asynchronous
interactions, they can start with BXXP. It's then their
responsibility to define the last 10% of the application protocol,
the part that does, as we say, "the useful work".
So, what does BXXP look like?
Mechanism BXXP
-------------- ----------------------------------------
Framing counting, with a trailer
Encoding MIME, defaulting to text/xml
Reporting 3-digit and localized textual diagnostic
Asynchrony channels
Authentication SASL
Privacy SASL or TLS
5.1 Framing and Encoding
Framing in BXXP looks a lot like SMTP or HTTP: there's a command line
that identifies the beginning of the frame, then there's a MIME
object (headers and body). Unlike SMTP, BXXP uses octet-counting,
but unlike HTTP, the command line is where you find the size of the
payload. Finally, there's a trailer after the MIME object to aid in
detecting framing errors.
Actually, the command line for BXXP has a lot of information, it
tells you:
o what kind of message is in this frame;
o whether there's more to the message than just what's in this frame
(a continuation flag);
Rose Informational [Page 17]
RFC 3117 On the Design of Application Protocols November 2001
o how to distinguish the message contained in this frame from other
messages (a message number);
o where the payload occurs in the sliding window (a sequence number)
along with how many octets are in the payload of this frame; and,
o which part of the application should get the message (a channel
number).
(The command line is textual and ends in a CR-LF pair, and the
arguments are separated by a space.)
Since you need to know all this stuff to process a frame, we put it
all in one easy to parse location. You could probably devise a more
efficient encoding, but the command line is a very small part of the
frame, so you wouldn't get much bounce from optimizing it. Further,
because framing is at the heart of BXXP, the frame format has several
consistency checks that catch the majority of programming errors.
(The combination of a sequence number, an octet count, and a trailer
allows for very robust error detection.)
Another trick is in the headers: because the command line contains
all the framing information, the headers may contain minimal MIME
information (such as Content-Type). Usually, however, the headers
are empty. That's because the BXXP default payload is XML [30].
(Actually, a "Content-Type: text/xml" with binary transfer encoding).
We chose XML as the default because it provides a simple mechanism
for nested, textual representations. (Alas, the 822-style encoding
doesn't easily support nesting.) By design, XML's nature isn't
optimized for compact representations. That's okay because we're
focusing on loosely-coupled systems and besides there are efficient
XML parsers available. Further, there's a fair amount of anecdotal
experience -- and we'll stress the word "anecdotal" -- that if you
have any kind of compression (either at the link-layer or during
encryption), then XML encodings squeeze down nicely.
Even so, use of XML is probably the most controversial part of BXXP.
After all, there are more efficient representations around. We
agree, but the real issue isn't efficiency, it's ease of use: there
are a lot of people who grok the XML thing and there are a lot of XML
tools out there. The pain of recreating this social infrastructure
far outweighs any benefits of devising a new representation. So, if
the "make" option is too expensive, is there something else we can
"buy" besides XML? Well, there's ASN.1/BER (just kidding).
Rose Informational [Page 18]
RFC 3117 On the Design of Application Protocols November 2001
In the early days of the SNMP [31], which does use ASN.1, the same
issues arose. In the end, the working group agreed that the use of
ASN.1 for SNMP was axiomatic, but not because anyone thought that
ASN.1 was the most efficient, or the easiest to explain, or even well
liked. ASN.1 was given axiomatic status because the working group
decided it was not going to spend the next three years explaining an
alternative encoding scheme to the developer community.
So -- and we apologize for appealing to dogma -- use of XML as the
favored encoding scheme in BXXP is axiomatic.
5.2 Reporting
We use 3-digit error codes, with a localized textual diagnostic.
(Each peer specifies a preferred ordering of languages.)
In addition, the reply to a message is flagged as either positive or
negative. This makes it easy to signal success or failure and allow
the receiving peer some freedom in the amount of parsing it wants to
do on failure.
5.3 Asynchrony
Despite the lessons of SMTP and HTTP, there isn't a lot of field
experience to rely on when designing the asynchrony features of BXXP.
(Actually, there were several efforts in 1998 related to application
layer framing, e.g., [32], but none appear to have achieved orbit.)
So, here's what we did: frames are exchanged in the context of a
"channel". Each channel has an associated "profile" that defines the
syntax and semantics of the messages exchanged over a channel.
Channels provide both an extensibility mechanism for BXXP and the
basis for parallelism. Remember the last parameter in the command
line of a BXXP frame? The "part of the application" that gets the
message is identified by a channel number.
A profile is defined according to a "Profile Registration" template.
The template defines how the profile is identified (using a URI
[33]), what kind of messages get exchanged, along with the syntax and
semantics of those messages. When you create a channel, you identify
a profile and maybe piggyback your first message. If the channel is
successfully created, you get back a positive response; otherwise,
you get back a negative response explaining why.
Perhaps the easiest way to see how channels provide an extensibility
mechanism is to consider what happens when a session is established.
Each BXXP peer immediately sends a greeting on channel zero
Rose Informational [Page 19]
RFC 3117 On the Design of Application Protocols November 2001
identifying the profiles that each support. (Channel 0 is used for
channel management -- it's automatically created when a session is
opened.) If you want transport security, the very first thing you do
is to create a channel that negotiates transport security, and, once
the channel is created, you tell it to do its thing. Next, if you
want to authenticate, you create a channel that performs user
authentication, and, once the channel is created, you tell it to get
busy. At this point, you create one or more channels for data
exchange. This process is called "tuning"; once you've tuned the
session, you start using the data exchange channels to do "the useful
work".
The first channel that's successfully started has a trick associated
with it: when you ask to start the channel, you're allowed to specify
a "service name" that goes with it. This allows a server with
multiple configurations to select one based on the client's
suggestion. (A useful analogy is HTTP 1.1's "Host:" header.) If the
server accepts the "service name", then this configuration is used
for the rest of the session.
To allow parallelism, BXXP allows you to use multiple channels
simultaneously. Each channel processes messages serially, but there
are no constraints on the processing order for different channels.
So, in a multi-threaded implementation, each channel maps to its own
thread.
This is the most general case, of course. For one reason or another,
an implementor may not be able to support this. So, BXXP allows for
both positive and negative replies when a message is sent. So, if
you want the classic client/server model, the client program should
simply reject any new message sent by the server. This effectively
throttles any asynchronous messages from the server.
Of course, we now need to provide mechanisms for segmentation and
flow control. For the former, we just put a "continuation" or "more
to come" flag in the command line for the frame. For the latter, we
introduced the notion of a "transport mapping".
What this means is that BXXP doesn't directly define how it sits of
top of TCP. Instead, it lists a bunch of requirements for how a
transport service needs to support a BXXP session. Then, in a
separate document, we defined how you can use TCP to meet these
requirements.
This second document pretty much says "use TCP directly", except that
it introduces a flow control mechanism for multiplexing channels over
a single TCP connection. The mechanism we use is the same one used
Rose Informational [Page 20]
RFC 3117 On the Design of Application Protocols November 2001
by TCP (sequence numbers and a sliding window). It's proven, and can
be trivially implemented by a minimal implementation of BXXP.
The introduction of flow control is a burden from an implementation
perspective -- although TCP's mechanism is conceptually simple, an
implementor must take great care. For example, issues such as
priorities, queue management, and the like should be addressed.
Regardless, we feel that the benefits of allowing parallelism for
intra-application streams is worth it. (Besides, our belief is that
few application implementors will actually code the BXXP framework
directly -- rather, we expect them to use third-party packages that
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?