12bet,高层浏览器网络api,协议和服务
自动管理的套接字池在所有浏览器进程间共享
套接字池规模一般都是6个套接字
自动化的套接字池会自动充用TCP连接,从而保障性能,此外,这种架构的优点有:
xhr,sse和websocket的对比
API | XHR | SSE | WebSocket |
---|---|---|---|
请求流 | 否 | 否 | 是 |
响应流 | 受限 | 是 | 是 |
分帧机制 | HTTP | 事件流 | 二进制分帧 |
二进制数据传输 | 是 | 否(base64) | 是 |
压缩 | 是 | 是 | 受限 |
应用传输协议 | HTTP | HTTP | WebSocket |
网络传输协议 | TCP | TCP | TCP |
XHR2新增:
服务器配置Access-Control-Allow-Origin
,但是这个请求有以下限制:
要启用 cookie 和 HTTP 认证,客户端发器请求是要加上withCredentials
属性,而且服务器必须以 Access-Control-Allow-Origin
首部响应,表示允许用户发送隐私数据。
如果客户端需要写或者读自定义的 HTTP 首部,或者想要使用“不简单的方法”发送请求,那么首先要获得服务器的许可,即先向第三方服务器发送一个 preflight 请求:
preflight 请求
OPTIONS /resource.js HTTP/1.1 // OPTIONS 请求(协商支持的方法)
Host: thirdparty.com
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: My-Custom-Header
...
preflight 响应
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT // 告诉客户端支持 GET, POST, PUT
Access-Control-Allow-Headers: My-Custom-Header // 告诉客户端支持 My-Custom-Header
...
协商之后才会正式发送跨域请求
12bet,浏览器可以自动解码的数据类型:
var xhr = new XMLHttpRequest();
xhr.open('GET', '/images/photo.webp');
xhr.responseType = 'blob';
xhr.onload = function() {
if (this.status == 200) {
var img = document.createElement('img');
img.src = window.URL.createObjectURL(this.response);
img.onload = function() {
window.URL.revokeObjectURL(this.src);
}
document.body.appendChild(img);
}
};
xhr.send();
send()
方法可以接受DOMString
,Document
,FormData
,Blob
,File
和ArrayBuffer
对象,自动完成相应的编码,并设置适当的内容类型(Content-Type),然后再分派请求
Blob可以分块发送:
var blob = new Blob(['sample content']);
const BYTES_PER_CHUNK = 1024 * 1024;
const SIZE = blob.size;
var start = 0;
var end = BYTES_PER_CHUNK;
while(start < SIZE) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
// 告诉服务器上传的数据范围(开始位置-结束位置/总大小)
xhr.setRequestHeader('Content-Range', start+'-'+end+'/'+SIZE);
xhr.send(blob.slice(start, end));
start = end;
end = start + BYTES_PER_CHUNK;
}
XHR进度事件:
事件类型 | 说明 | 触发次数 |
---|---|---|
loadstart | 传输开始 | 一次 |
progress | 正在传输 | 零或多次 |
error | 传输出错 | 零或多次 |
abort | 传输终止 | 零或多次 |
load | 传输成功 | 零或多次 |
loadend | 传输完成 | 一次 |
var xhr = new XMLHttpRequest();
xhr.open('GET','/resource');
xhr.timeout = 5000;
xhr.addEventListener('load', function() { ... });
xhr.addEventListener('error', function() { ... });
var onProgressHandler = function(event) {
if(event.lengthComputable) {
var progress = (event.loaded / event.total) * 100;
... }
}
// 上传进度
xhr.upload.addEventListener('progress', onProgressHandler);
// 下载进度
xhr.addEventListener('progress', onProgressHandler);
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open('GET', '/stream');
xhr.seenBytes = 0;
xhr.onreadystatechange = function() {
if(xhr.readyState > 2) {
// 获取新数据
var newData = xhr.responseText.substr(xhr.seenBytes);
// 处理新数据
// ...
// 更新处理的字节偏移量
xhr.seenBytes = xhr.responseText.length;
}
};
xhr.send();
基于XHR的流实现麻烦,效率很低,而且缺乏规范,总之不适合用于实现流式数据处理
API:
var source = new EventSource("/path/to/stream-url");
source.onopen = function () { ... };
source.onerror = function () { ... };
source.addEventListener("foo", function (event) {
processFoo(event.data);
});
source.onmessage = function (event) {
log_message(event.id, event.data);
if (event.id == "CLOSE") {
source.close();
}
};
var ws = new WebSocket('wss://example.com/socket');
ws.onerror = function (error) { ... }
ws.onclose = function () { ... }
ws.onopen = function () {
ws.send("Connection established. Hello server!");
};
ws.onmessage = function(msg) {
if(msg.data instanceof Blob) {
processBlob(msg.data);
} else {
processText(msg.data);
}
};
接收文本和二进制数据
var ws = new WebSocket('wss://example.com/socket');
// 如果接收二进制数据,强制转换为``ArrayBuffer``
ws.binaryType = "arraybuffer";
ws.onmessage = function(msg) {
if(msg.data instanceof ArrayBuffer) {
processArrayBuffer(msg.data);
} else {
processText(msg.data);
}
};
发送文本和二进制数据
var ws = new WebSocket('wss://example.com/socket');
ws.onopen = function () {
// 发送文本
socket.send("Hello server!");
// 发送json
socket.send(JSON.stringify({'msg': 'payload'}));
// 发送二进制ArrayBuffer
var buffer = new ArrayBuffer(128);
socket.send(buffer);
// 发送二进制ArrayBufferView
var intview = new Uint32Array(buffer);
socket.send(intview);
// 发送Blob
var blob = new Blob([buffer]);
socket.send(blob);
}
子协议协商
// 告诉服务器客户端支持 appProtocol, appProtocol-v2 协议
var ws = new WebSocket('wss://example.com/socket', ['appProtocol', 'appProtocol-v2']);
ws.onopen = function () {
// 判断服务器是不是使用的 appProtocol-v2
if (ws.protocol == 'appProtocol-v2') {
// ...
} else {
// ...
}
}
队首阻塞:websocket容易发生队首阻塞,因为消息可能被分成一或多个帧,但是不同消息的帧不能交错发送,因为没有与http 2.0分帧机制中“流ID”对等的字段。
websocket不支持多路复用,所以每个websocket连接都需要一个专门的TCP连接
协议拓展:
HTTP升级协商的首部:
Sec-WebSocket-Version
:客户端发送,想使用的websocket协议版本Sec-WebSocket-Key
:客户端发送,自动生成的键,验证服务器支持请求的协议版本Sec-WebSocket-Accept
:服务器响应,包含Sec-WebSocket-Key
的签名值,证明它支持的请求的协议版本Sec-WebSocket-Protocol
:用于协商应用子协议:客户端发送支持的协议列表,服务器必须只回应一个协议名Sec-WebSocket-Extensions
:用于协商本次连接要使用的websocket拓展:客户端发送支持的拓展,服务器通过返回相同的首部确认自己支持一或多个拓展主要有以下要点: