- From: Randell Jesup <randell-ietf@jesup.org>
- Date: Mon, 23 Apr 2012 12:01:05 -0400
- To: "public-webrtc@w3.org" <public-webrtc@w3.org>
- CC: Michael Tuexen <tuexen@fh-muenster.de>, Randall Stewart <rrs@lakerest.net>, Salvatore Loreto <salvatore.loreto@ericsson.com>
In going over the details for the DataChannel minor protocol and how it
works over SCTP, we realized we'll need a 3-way handshake instead of a
2-way. The reason is that an OPEN_RESPONSE sent ordered followed by a
data message sent unordered would allow the data message to come in at
the other end before the open response, and since we don't know the
input-stream -> output-stream mapping until we get the OPEN_RESPONSE, we
might not know what to do with it. So we plan to add an ACK message to
the protocol to provide the 3-way handshake.
We now have to decide what to do about the JS-level API; which to recap
currently requires waiting for onopen on the opener's side, and allows
the answerer to send() immediately on getting ondatachannel.
We've figured out that we can allow send() before onopen (in either
direction) by forcing data to be in-order until the 3-way handshake is
complete. Obviously, if you're using in-order (reliable or unreliable)
this doesn't matter. It does cause and data sent to be buffered in the
stack(s) until it can be delivered, which typically would be no real
problem unless the OPEN (or OPEN_RESPONSE) was lost and had to be
re-transmitted.
There are two really "simple and clean" solutions:
1) both sides wait for onopen()
2) neither side waits for onopen()
Note that in #2 we'd still have onopen() for ease of porting WebSockets
code to it DataChannels.
We could also keep the current API (onopen at the opener, immediate on
the receiver).
#1 requires the application maintain state in a number of cases where it
would not otherwise need to, or buffer within the app. I.e. an
application that wants to send data on a channel not yet open must open
it and somehow defer the sending until it's open. This can be
encapsulated in JS functions:
temp = peer.createDataChannel(..)
temp.data_to_send = "I want to send just this";
temp.onopen = function() {
temp.send(temp.data_to_send);
temp.close(); // don't wait for GC to close/reclaim stream!
temp = null; // available for GC
};
Of course, not as straightforward as:
temp = peer.createDataChannel(..)
temp.send("I want to send just this");
temp.close();
temp = null;
but not a lot worse. It's more complex if you might want to send more
data to the channel and need to buffer all of it in the app (think a log
channel) while waiting for it to open. Commonly the answerer will wait
for incoming data before sending anything, but not in all cases.
It seems to me that forcing in-order delivery until the handshake is
complete is simpler for the user, and only rarely has any impact on the
application. The exception might be if you wanted to dump a bunch of
sends that were reliable and out-of-order immediately on creation of the
channel - and I don't see it being a major issue there - and you can
still wait on onopen if you want.
Recap: #2 is slightly closer to how WebSockets works (though anything
coded for WebSockets' onopen behavior would still work in #1), and #2
avoids potential additional buffering in the stack if the
OPEN/OPEN_RESPONSE is lost. (If it's not lost, there's really no extra
buffering occurring, except maybe in the rare case where we need to
increase the maximum number of simultaneous streams.)
Opinions?
--
Randell Jesup
randell-ietf@jesup.org
Received on Monday, 23 April 2012 16:02:20 UTC