- From: Randell Jesup <randell-ietf@jesup.org>
- Date: Fri, 31 Aug 2012 17:05:35 -0400
- To: "public-webrtc@w3.org >> \"public-webrtc@w3.org\"" <public-webrtc@w3.org>
So, on a totally different topic: We discussed the DataChannels API at the Stockholm Interim. I'd like to move this part of the spec forward, as Mozilla has an implementation in testing. The API, per the decisions at the Interim and mailing list is based closely on the WebSockets interface. For example, you are supposed to wait for onopen to fire before calling send(), at both ends. (The low-level IETF protocol supports sending before onopen, but WebSockets doesn't.) Currently I have no limit on send(DOMString); WebSockets limits DOMStrings to 123 characters. I should note that the immediately-following IETF Interim appeared to have the opposite conclusion on onopen and send(), and so the IETF protocol design allows sending immediately, while the API I propose for WebRTC DataChannels here does not. Supported channel types are reliable (and and out of order) channels, unreliable and partly-reliable channels. (See the dictionary below.) I'd *REALLY* like to move forward on resolving the open issues listed below, as we're preparing to expose a preliminary DataChannels implementation. Thanks. Open Issues: ========== When can createDataChannel() be called? ---------------------------------------------------------- Currently pc.createDataChannel() must be called after onconnected fires. This was a point of discussion at the meeting and on the rtcweb list, since DataChannels need some RTTs to establish; allowing the application to pre-request data channels would allow them to be included in the signaling (and thus save 1 RTT off the setup time). In order to allow pc.createDataChannel() to be called earlier the only difference at the offerer's JS API level would be whether you can do: pc = new PeerConnection(...); pc.addStream(...); pc.createDataChannel("foo",{}); pc.createOffer(...); etc. On the receive side, there's no way to reject a DataChannel (just as there's no way to reject one before it's created after the PeerConnection is connected). You can avoid setting onDataChannel or respond to onDataChannel by closing it if you don't want it. The receiver would be able to call pc.createDataChannel() before createAnswer(). In all cases the channel isn't usable until onopen fires. Do we need a DataConnection object? ----------------------------------------------------- It was suggested that we have a DataConnection object, and hang createDataChannel off of that. (see the thread "DataConnection objects" from June.) Doing so would minorly reduce the number of methods on PeerConnection (by moving onconnection/onclosedconnection/ondatachannel to it, and maybe an attribute for initial number of streams). Pros: * It gives you a clear way to easily drop all DataChannels. * It gives you an easy point to query the association (mostly for maxstreams I'd guess, perhaps for total bytes queued across all DataChannels). * A spec for a DataConnection object might be useful in some other context in the future - but it always could be added later if needed. (You could even indicate that PeerConnection implements DataConnection if you wanted.) * It does give you a clear way to say "Don't bother to open an association" if you don't need one. * You could add more DataConnections to a PeerConnections (but what does that gain you?) * Allows future extension to allow other protocols to be used over the connection/association. * Gives you something to hang an application identifier off of - but the application can do that in other ways. Cons: * More objects, more code required to set up a call with little benefit to the current usecases. * If you want to add a DataConnection later, it will require a renegotiation * Answering requires a few extra steps to see if the offerer offered a DataConnection. Overall, I'm very mildly in favor of adding the DataConnection object, because it might be useful in some other contexts in the future. I do think it complicates PeerConnection slightly, but only slightly (less methods, but PeerConnection now needs to trigger renegotiation when createDataConnection is called.) Label glare: --------------- A previous discussion ("Data API symmetry", in April/May) covered this. This is the "what happens if both sides call createDataChannel("foo",{}) at the same time" question. You can: 1) Create two channels labelled "foo". Each side would get an onopen to their createDataChannel, and each would get an onDataChannel and onopen for the one created by the other side. Handling the glare would be the application's domain. 2) Fail both (or find some agreed-on tiebreaker that lets you have one fail and the other succeed). 3) Create one channel labelled "foo", and each side would believe they created it. Both would simply get "onopen", and it might even come faster than normal. NOTE: if the two sides select different options for the channel, then you still may need to return errors! (or create channels with the same label) #3 is mildly appealing, until you think about how you handle errors with disagreement on reliability. So I think I end up preferring #1. #3 also implies you should have a unique label for each channel, to avoid confusions with opening multiple channels with the same name at the same time when the other side tries to as well. Attributes: ------------- Is the single "reliable" attribute enough? Do we need to expose the dict entries? Do we need a FindDataChannel(label) to get a reference to an existing channel? (My opinion: Probably no to all of these, though I can see exposing the dict entries.) This is the dictionary for RTCPeerConnection's createDataChannel() method: (xpidl) /* If either maxRetransmitTime or maxRetransmitNum are set, it's unreliable, else it's a reliable channel. If both are set it's an error. outOfOrderAllowed can be used with any type of channel. The equivalent of UDP is { outOfOrderAllowed: true, maxRetransmitNum: 0 }. The TCP equivalent is {}. */ dictionary DataChannelInit { boolean outOfOrderAllowed; unsigned short maxRetransmitTime; // in ms unsigned short maxRetransmitNum; }; And in PeerConnection: (xpidl) /* Data channel */ nsIDOMDataChannel createDataChannel([optional] in ACString label, /* DataChannelInit */ [optional] in jsval options); attribute RTCPeerConnectionCallbackVoid onConnection; attribute RTCPeerConnectionCallbackVoid onClosedConnection; attribute RTCPeerConnectionCallback onDataChannel; This is the webidl for DataChannel I have currently (note: it might have errors as we use xpidl internally, though we're switching to webidl). interface DataChannel : EventTarget { [Infallible] readonly attribute DOMString label; [Infallible] readonly attribute boolean reliable; // ready state const unsigned short CONNECTING = 0; const unsigned short OPEN = 1; const unsigned short CLOSING = 2; const unsigned short CLOSED = 3; [Infallible] readonly attribute unsigned short readyState; [Infallible] readonly attribute unsigned long bufferedAmount; [TreatNonCallableAsNull, GetterInfallible] attribute Function? onopen; [TreatNonCallableAsNull, GetterInfallible] attribute Function? onerror; [TreatNonCallableAsNull, GetterInfallible] attribute Function? onclose; [Infallible] readonly attribute DOMString extensions; [Infallible] readonly attribute DOMString protocol; void close(); // messaging [TreatNonCallableAsNull, GetterInfallible] attribute Function? onmessage; [GetterInfallible] attribute DOMString binaryType; void send(DOMString data); void send(Blob data); void send(ArrayBuffer data); }; -- Randell Jesup randell-ietf@jesup.org
Received on Friday, 31 August 2012 21:06:39 UTC