webRTC结合webSocket实时通信

4,700 阅读4分钟

webocket

WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双向通信的通道。

为什么传统的HTTP协议不能做到WebSocket实现的功能?这是因为HTTP协议是一个请求-响应协议,请求必须先由浏览器发给服务器,服务器才能响应这个请求,再把数据发送给浏览器。换句话说,浏览器不主动请求,服务器是没法主动发数据给浏览器的。

为什么WebSocket连接可以实现全双工通信而HTTP连接不行呢?实际上HTTP协议是建立在TCP协议之上的,TCP协议本身就实现了全双工通信,但是HTTP协议的请求-应答机制限制了全双工通信。

webSockert使用

常用事件监听

webSocket.onopen

用于指定连接成功后的回调函数

webSocket.onmessage

用于指定当从服务器接受到信息时的回调函数

webSocket.onclose

用于指定连接关闭后的回调函数

webSocket.onerror

用于指定连接失败后的回调函数

常用方法

webSocket.close()

关闭当前链接

webSocket.send()

向服务器发送数据,当前webSocket暂时只支持发送字符串以及二进制数据。

示例

<!--解决大部分浏览器适配的问题-->
var sc = document.createElement('script')
sc.src = 'https://webrtc.github.io/adapter/adapter-latest.js'
document.head.appendChild(sc)
/**
 *@target 建立webSocket 连接
 *@ param url   webSocket服务器连接地址,如:ws://localhost/ws
 */
var ws = new WebSocket(url)
// 监听当前ws连接事件
ws.onopen = function() {
	// 处理连接成功回调函数
}
ws.onclose = function() {
	// 处理关闭当前连接回调函数
}
ws.onerror = function() {
	// 处理当前连接异常信息
}
ws.onmessage = function(e) {
	// 获取服务端发送的数据
	var data = e.data
	// 处理服务端发送的数据
}
// 向服务端发送数据
ws.send('data')
// 关闭当前连接
ws.close()

通过以上方法我们已经可以实现实时通信的功能,如果我们需要实现实时语音以及视频通信,可结合WebRtc和getUserMedia的使用。

getUserMedia

用户许可使用视频和/或音频输入设备(例如相机)或共享屏幕和/或麦克风。如果用户提供了权限,则将通过Promise对象返回MediaStream。如果用户拒绝许可,或者多媒体资源无效,则通过PermissionDeniedError或NotFoundError分别拒绝承诺。

MediaStream

getTracks()

返回一个序列,该序列表示MediaStreamTrack 中的所有track对象

track.stop()

关闭已启用的本地摄像头以及音频功能,使用方法:

    MediaStream.getTracks().forEach(function (track) {
        track.stop()
    })

WebRtc

WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。

WebRTC实现了基于网页的视频会议,标准是WHATWG 协议,目的是通过浏览器提供简单的javascript就可以达到实时通讯(Real-Time Communications (RTC))能力。

常用事件监听

onicecandidate

本地ICE代理需要通过信令服务器将消息传递给另一个对等方时,就会触发这种情况

常用方法

addTrack

将新的媒体轨道添加到轨道集,该轨道将被发送到另一对等方。

createOffer

启动SDP的创建,以启动与远程对等方的新WebRTC连接。SDP协议包括有关MediaStreamTrack已附加到WebRTC会话,解码器和浏览器支持的选项的所有信息,以及ICE代理已经收集的任何候选者的信息,目的是通过信令通道发送给潜在的对等方以请求连接或更新现有连接的配置。

createAnswer

创建关于从远程对等方收到的SDP而产生的本地SDP对象,包含有关会话中已附加的任何媒体,浏览器支持的编解码器和选项以及已收集的所有ICE候选者的信息

setLocalDescription

更改与连接关联的本地描述。此描述指定连接本地端的属性,包括媒体格式。

addIceCandidate

将新的远程候选者添加到RTCPeerConnection的远程描述中,该描述描述了连接的远程端的状态。

setRemoteDescription

指定连接的远程端的属性,包括媒体格式。

视频、语音通信

通过以上的介绍,我们可以开始结合getUserMedia、webbRtc、webSocket三者实现语音或视频通话。

公共约定

type: 数据请求类型

data: 数据

from: 发送方标识符

to: 接受方标实符

实现流程

实现代码

var sc = document.createElement('script'), pc = null, to = ''
sc.src = 'https://webrtc.github.io/adapter/adapter-latest.js'
document.head.appendChild(sc)
var ws = new WebSocket(url)
ws.onmessage = function(e) {
	var data = JSON.parse(e.data)
	var type = data.type
	if(type === 'invite') {
	    to = data.from
	    createRTC(data.data, true)
	} else if (type === 'recive') {
	    to = data.from
	} else if (type === 'canditate') {
	    pc.addIceCandidate(new RTCIceCandidate(JSON.parse(data.data)))
	} else if (type === 'offer') {
	    pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(data.data)))
	    pc.createAnswer().then(function(answer) {
          return pc.setLocalDescription(answer)
        }).then(sdp => {
            send({type:'answer',data:sdp})
        })
	} else if (type === 'answer') {
	    pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(data.data)))
	}
}
function createRTC(video, judge) {
    var iceServer = 'null' || '自定义'
    pc = new RTCPeerConnection(iceServer)
    if (judge) {
        pc.onicecandidate = function (e) {
            if (e.candidate) {
                send({type: 'canditate', data: e.candidate})
            }
        }
    pc.onaddtrack = function(e) {
        // 远程音频/视频流回调
    }
    navigator.mediaDevices.getUserMedia({audio: true, video: !!video}).then(steam => {
        stream.getTracks().forEach(track => {
            pc.addTrack(track, stream)
        })
        if (judge) {
            pc.createOffer().then(sdp => {
                send({type: 'offer',data:sdp})
            })
        }
    })
}
function send(data) {
    data.to = data.to || to
    ws.send(JSON.stringify(data))
}
<!--发送视频通话-->
ws.send({type: 'invite', data: 'video',from: 'from', to: 'to'})
createRTC(true)
<!--发送语音通话-->
ws.send({type: 'invite', data: '',from: 'from', to: 'to'})
createRTC()