开始点对点!
我们终于准备好了! 两个对等方已成功交换会话描述和网络可达性信息。 借助信令服务器的中介,已经正确设置和配置了两个 PeerConnection
对象。 如 图5-16 所示,双向多媒体通信通道现在可用作两个浏览器之间的直接传输工具。 现在服务器已完成其任务,并且此后将被两个通信对等方完全绕开。
图5-16 点对点建立后进行通讯
成功进行渠道协商后,图5-17 和 图5-18 中的两个快照分别显示了 Joiner 和 Initiator 的窗口。 您可以在两个图中看到,每个对等方现在都具有可用的本地视图和远程视图,以及可以分别用于向远程用户发送直接消息和记录从远程用户接收的直接消息的两个文本区域。
图5-17 在 Chrome 中建立了通信:Joiner
图5-18 在 Chrome 中建立通信:Initiator
使用 Data Channel
在本小节中,我们将深入研究配置和使用数据通道的细节。实际上,数据通道是由启动器创建的,它是 createPeerConnection()
函数代码的一部分:
function createPeerConnection() {
try {
pc = new RTCPeerConnection(pc_config, pc_constraints);
...
} catch (e) {
...
}
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
if (isInitiator) {
try {
// Create a reliable data channelsendChannel = pc.createDataChannel("sendDataChannel",{
reliable: true});
trace('Created send data channel');
} catch (e) {
...
}
sendChannel.onopen = handleSendChannelStateChange;
sendChannel.onmessage = handleMessage;
sendChannel.onclose = handleSendChannelStateChange;
} else {
// Joiner
pc.ondatachannel = gotReceiveChannel;
}
}
上面的代码片段显示了如何将多个处理程序与数据通道相关联。 例如,我们在 handleSendChannelStateChange()
函数下方显示该函数,该函数负责在通道达到打开状态后立即启用发送者的文本区域和 “发送” 按钮:
function handleSendChannelStateChange() {
var readyState = sendChannel.readyState;
trace('Send channel state is: ' + readyState);
if (readyState == "open") {
dataChannelSend.disabled = false;
dataChannelSend.focus();
dataChannelSend.placeholder = "";
sendButton.disabled = false;
} else {
dataChannelSend.disabled = true;
sendButton.disabled = true;
}
}
下面显示的 sendData()
JavaScript 函数配置为 “发送” 按钮的处理程序,并执行以下操作:(1)收集用户在 sendTextArea
中插入的文本; (2)通过实例化数据通道发送此类文本。
// Handler associated with Send button
sendButton.onclick = sendData;
...
function sendData() {
var data = sendTextarea.value;
if(isInitiator) sendChannel.send(data);
else receiveChannel.send(data);
trace('Sent data: ' + data);
}
图5-19 显示了通过数据通道发送文本消息之后的发起方窗口。
图5-19 使用数据通道:Initiator
消息到达另一侧后,将触发 handleMessage()
函数,如下所示,该函数仅获取已传输的数据并将其记录在 HTML 页面的 receiveTextArea
元素中:
function handleMessage(event) {
trace('Received message: ' + event.data);
receiveTextarea.value += event.data + '\n';
}
这也显示在 图5-20 中的快照中。
图5-20 使用数据通道:加入方
转到接收频道, Joiner 的浏览器引发 dataChannel
事件后,就会激活 getReceiveChannel()
函数。 该处理程序设置接收通道并正确配置它以管理与通道相关的事件:
function gotReceiveChannel(event) {
trace('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.onmessage = handleMessage;
receiveChannel.onopen = handleReceiveChannelStateChange;
receiveChannel.onclose = handleReceiveChannelStateChange;
}
图5-21 和 图5-22 分别显示了 Joiner 通过数据通道将答案发送回 Initiator , Initiator 接收答案并将其记录在数据通道文本区域内。
图5-21 Data channel: Joiner 回答 Initiator 的消息
图5-22 Data channel: Initiator 获得 Joiner 的回答