切换至深色主题
Ajax-Fetch-socket-WebRTC
重要通知
。
基本概况
- 官网:<>
- GitHub:<>
Navigator.sendBeacon
navigator.sendBeacon() 方法可用于通过 HTTP POST 将少量数据 异步 传输到 Web 服务器。
代码示例: 用户行为分析上报
避免使用 unload 和 beforeunload,在不同终端设备上不可靠。
安全与漏洞
没有对上传文件扩展名进行限制或者限制可以被绕过,允许用户上传文件可能会让攻击者注入危险内容或恶意代码,并在服务器上运行,以及危及数据库等;
解决措施
- 校验文件格式
- 通过文件头校验文件格式
- 文件大小校验
- 文件重命名
文件对象数据示例
var ID = document.getElementsByTagName("input[type='file']")[0];
let content = file.files[0];
console.log(content);
console.log(content.name);
console.log(content.size);
console.log(content.type);
File {
lastModified: 1577680954000
lastModifiedDate: Mon Dec 30 2019 12:42:34 GMT+0800 (China Standard Time) {}
name: "BaiduShurufa_2019-12-30_20-41-13.jpg"
size: 491540
type: "image/jpeg"
webkitRelativePath: ""
}客户端过滤限制
服务器过滤限制
// 单文件
let file = ctx.request.files;
console.log(file.content); //{size: , path: , name: , type: , }
// 多文件
let file = ctx.request.files;
Array.from(file.content).forEach(async (idx)=> {
//await saveFile(idx);
});SQL过滤限制
FormData
let form = new FormData();
form.append('data', 'sex1');
form.append('data', 'sex2');
form.append('name', 'FormData');
console.log(form.get('data')); //sex1
console.log(form.getAll('data')); //["sex1", "sex2"]
console.log(form.has('data')); //true
for (let i of form.keys()) {
console.log(i); //键名:data、data、name
}
for (let i of form.values()) {
console.log(i); //键值:sex1、、
}
form.forEach((dx)=> {
console.log(dx); //键值:sex1、、
});
for (let i of form.entries()) {
console.log(i); //键对:["data", "sex1"]、、、
}HTTP状态码
----------------------------------------------------------------------------------
状态码 消息 描述
----------------------------------------------------------------------------------
100 Continue 只有一部分请求被服务器接收,但只要没被服务器拒绝,客户端就会延续这个请求
101 Switching Protocols 服务器交换机协议
200 OK 请求被确认
201 Created 请求时完整的,新的资源被创建
202 Accepted 请求被接受,但未处理完
203 Non-authoritative Information
204 No Content
205 Reset Content
# https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/206
206 Partial Content 成功状态响应代码表示请求已成功,并且主体包含所请求的数据区间,该数据区间是在请求的 Range 首部指定的
300 Multiple Choices 一个超链接表,用户可以选择一个超链接并访问,最大支持5个超链接
301 Moved Permanently 被请求的页面已经移动到了新的URL下
302 Found 被请求的页面暂时性地移动到了新的URL下
303 See Other 被请求的页面可以在一个不同的URL下找到
304 Not Modified HTTP缓存机制,即在协商缓存过程中,表示没有修改,
305 Use Proxy
306 Unused 已经不再使用此状态码,但状态码被保留
307 Temporary Redirect 被请求的页面暂时性地移动到了新的URL下
# 请求错误
400 Bad Request 服务器无法识别请求
401 Unauthorized 被请求的页面需要用户名和密码
402 Payment Required 目前还不能使用此状态码
403 Forbidden 禁止访问所请求的资源
404 Not Found 服务器无法找到所请求的页面
405 Method Not Allowed 请求中所指定的方法不被允许
406 Not Acceptable 服务器只能创建一个客户端无法接受的响应
407 Proxy Authentication Required 在请求被服务前必须认证一个代理服务器
408 Request Timeout 请求时间超过了服务器所能等待的时间,连接被断开
409 Conflict 请求有矛盾的地方
410 Gone 被请求的页面不再可用
411 Length Required "Content-Length" 没有被定义,服务器拒绝接受请求
412 Precondition Failed 请求的前提条件被服务器评估为false
413 Request Entity Too Large 因为请求的实体太大,服务器拒绝接受请求
414 Request-url Too Long 服务器拒绝接受请求,因为URL太长。多出现在把"POST"请求转换为"GET"请求时所附带的大量查询信息
415 Unsupported Media Type 服务器拒绝接受请求,因为媒体类型不被支持
417 Expectation Failed
# 服务器错误
500 Internal Server Error 请求不完整,服务器遇见了出乎意料的状况
501 Not Implemented 请求不完整,服务器不提供所需要的功能
502 Bad Gateway 请求不完整,服务器从上游服务器接受了一个无效的响应
503 Service Unavailable 请求不完整,服务器暂时重启或关闭
504 Gateway Timeout 网关超时
505 HTTP Version Not Supported 服务器不支持所指定的HTTP版本
----------------------------------------------------------------------------------Fetch
- MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
- GitHub:https://github.com/github/fetch
简介与核心思想
- 当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
- fetch() 不会接受跨域 cookies;你也不能使用fetch() 建立起跨域会话。其他网站的Set-Cookie头部字段将会被无视。
- fetch 不会发送 cookies。除非你使用了credentials的 初始化选项。(自2017年8月25日以后,默认的credentials政策变更为same-origin。Firefox也在61.0b13版本中,对默认值进行修改)
重大警告
- Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次,即Response.body仅能被读一次,如需多次读取,需要重新设计其流,见public/fetch.js
- 如果'Content-Type' = 'application/x-www-form-urlencoded',则body的传值必须为:body: name=eshen&sex=男
简易示例
fetch("http://example.com/movies.json")
.then((response) => response.json())
.then((data) => console.log(data));结构示例
const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');
formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);
fetch(
url,
options: {
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/x-www-form-urlencoded',
'content-type': 'multipart/form-data',
'content-type': 'application/json',
'content-type': 'text/plain'
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
// JSON
body: JSON.stringify(data), // must match 'Content-Type' header
// 上传文件
body: formData,
// 这个属性非常特殊,keepalive 选项可以允许请求持续保持连接,甚至页面已经关闭的情况。使用 keepalive 标志的 Fetch 是 Navigator.sendBeacon() API 的一种替代方案。
keepalive: true
}
)
.then((res)=> {
// res.arrayBuffer();
// res.blob();
// res.json();
// res.text()
// res.formData();
return res;
})
.then((res)=> {
if (res.ok) {
return res;
}
throw new Error(res);
})
.then((res) => {
const reader = res.body.getReader();
const readData = await reader.read();
})
.catch((err)=> {
//
});Interfaces
fetch('https://api.ysunlight.com/api/get')
.then((res) => {
return res.json();
}).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err, 'Error');
});Polyfill
Fetch API 的支持情况,可以通过检测Headers, Request, Response 或 fetch()是否在Window 或 Worker 域中。
if(Window.fetch) {
// run my fetch request here
} else {
// do something with XMLHttpRequest?
}Body
不管是请求还是响应都能够包含 body 对象。body 也可以是以下任意类型的实例。
- ArrayBuffer
- ArrayBufferView (Uint8Array 等)
- Blob/File
- string
- URLSearchParams
- FormData
基础方法
Body 类定义了以下方法(这些方法都被 Request 和 Response所实现)以获取 body 内容。这些方法都会返回一个被解析后的 Promise 对象和数据。
- Request.arrayBuffer() / Response.arrayBuffer()
- Request.blob() / Response.blob()
- Request.formData() / Response.formData()
- Request.json() / Response.json()
- Request.text() / Response.text()
完整示例
/**
* Author: 王军
* Email: ysungod@163.com
*
const { status, data } = await fetchAPI({
path: '',
method: 'post',
data: {},
headers: {}
})
.then(res => res)
.catch(err => err);
*/
export interface OPTIONS {
base?: string;
path: string;
data?: Record<string, any>;
headers?: Record<string, any>;
method?: "get" | "post";
uploadType?: "api" | "file";
dataType?: "formData" | "wwwForm" | "json" | "text";
responseType?: "response" | "string" | "arrayBuffer" | "blob" | "json";
mode?: "cors" | "no-cors" | "same-origin";
progress?: (...args: any[]) => void;
}
export function fetchAPI(options = {} as OPTIONS) {
const base = options.base || "";
const path = options.path || "";
const data = options.data || {};
const headers = options.headers || {};
// 请求方法类型,默认为'PUT'
const method = (options.method || "GET").toUpperCase();
// 'file' | 'api'
const uploadType = options.uploadType || "api";
// 请求数据类型,默认为'json', 枚举列表: json | text | string | FormData
const dataType = options.dataType || "json";
// 响应数据类型,默认为'json'
const responseType = options.responseType || "json";
// cors、no-cors 或者 same-origin
const mode = options.mode || "cors";
const basePath =
path.includes("https://") || path.includes("http://") ? path : base + path;
const CONTENT_TYPE = {
formData: "multipart/form-data",
wwwForm: "application/x-www-form-urlencoded",
json: "application/json",
text: "text/plain",
};
// 屏蔽上传文件,上传文件会自动携带Content-Type
if (uploadType !== "file" && !headers["Content-Type"]) {
headers["Content-Type"] = CONTENT_TYPE[dataType];
}
const params: Record<string, any> = {
method,
mode,
headers: {
...headers,
},
};
let requestAPI = basePath;
if (method === "GET") {
const url = new URL(basePath);
const pathString = url.origin + url.pathname;
const defineParams: Record<string, any> = {};
for (const [key, value] of url.searchParams) {
defineParams[key] = value;
}
const searchParams = new URLSearchParams(Object.assign(defineParams, data));
requestAPI = pathString + "?" + searchParams.toString();
}
if (method === "POST") {
if (headers["Content-Type"] === CONTENT_TYPE.wwwForm) {
const searchParams = new URLSearchParams(data);
params["body"] = searchParams.toString();
} else {
params["body"] = uploadType === "api" ? JSON.stringify(data) : data;
}
}
return new Promise((resolve) => {
fetch(requestAPI, {
...params,
})
.then((response) => {
return { body: response.body, response: response }
})
.then(({ body, response }) => {
// 若跨域请求,则获取不到文件字节长度。
const filesize = +(response as any).headers.get('content-length');
// 获取流
const reader = (body as any).getReader();
// 已下载字节
let loadedLength = 0;
return new ReadableStream({
start(controller) {
return pump();
function pump(): any {
return reader.read().then(({ done, value }: { done: any; value: any }) => {
// 读不到更多数据就关闭流
if (done) {
controller.close();
return;
}
// 已下载的字节
loadedLength += value.length;
// 下载进度
const progress = Math.floor((loadedLength / filesize) * 100);
options?.progress && options?.progress(progress)
// 将下一个数据块置入流中
controller.enqueue(value);
return pump();
});
}
}
});
})
.then(stream => new Response(stream))
.then((response) => {
// 响应数据类型
switch (responseType) {
case "response":
return resolve(response);
case "string":
return resolve(response.text());
case "arrayBuffer":
return resolve(response.arrayBuffer());
case "blob":
return resolve(response.blob());
default:
resolve(response.json());
}
})
.catch(() => {
resolve(undefined);
});
});
}cross-fetch
# 安装配置
npm install --save cross-fetchAJAX
AJAX,全写为Asynchronous JavaScript and XML,即异步的 JavaScript 和 XML。它属于一种使用现有标准的新方法,在不重新加载整个页面的情况下,实现了与服务器交换数据并更新部分网页的功能。
- https://developer.mozilla.org/zh-CN/docs/Web/Guide/AJAX
- https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
- https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API
- ArrayBuffer:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
背景与发展
- 大概在1998年,允许客户端脚本发送HTTP请求(XMLHTTP)的第一个组件由Outlook Web Access小组写成,该组件原属于微软 Exchange Server,并且迅速地成为了 Internet Explorer 4.0 的一部分。
- 2005 年初,许多事件使得 Ajax 被大众所接受。Google 在它著名的交互应用程序中使用了异步通讯,如Google、Google 地图、Google 搜索框架应用及对ajax框架的思考框架应用及对ajax框架的思考建议、Gmail等。
- 2010年以后,AJAX在互联网的工作中越来越扮演着重要的角色,尤其是著名的软件设计理念,前后端分离设计,由此诞生了专业前端工作岗位,并承担了互联网工作中新一代的软件设计风格与架构。
XMLHttpRequest
# 属性与方法
const xhr = new XMLHttpRequest();
for (let i in xhr) {
console.log(i + '=' + xhr[i]);
}
> UNSENT=0
> OPENED=1
> HEADERS_RECEIVED=2
> LOADING=3
> DONE=4
# 设置请求参数
> timeout=0
> responseType= # 设置响应类型
> withCredentials=false # 跨域时认证信息是否展示在cookie里?true:浏览器会将cookie自动加在request header中
> setRequestHeader=function setRequestHeader() { [native code] }
# 获取响应参数
> getResponseHeader=function getResponseHeader() { [native code] }
> getAllResponseHeaders=function getAllResponseHeaders() { [native code] }
> overrideMimeType=function overrideMimeType() { [native code] }
# 事件
> onreadystatechange=null #
# 数据内容
> readyState=0
> status=0
> responseURL=
> statusText=
> response=
> responseText= # 获得字符串形式的响应数据
> responseXML=null # 获得 XML 形式的响应数据
# 请求操作
> open=function open() { [native code] }
> send=function send() { [native code] }
> abort=function abort() { [native code] }
# 进程
> onloadstart=null
> onprogress=null # 响应阶段
> onload=null
> onloadend=null
> ontimeout=null
> onabort=null
> onerror=null
> addEventListener=function addEventListener() { [native code] }
> removeEventListener=function removeEventListener() { [native code] }
> dispatchEvent=function dispatchEvent() { [native code] }
# 上传阶段
> upload=[object XMLHttpRequestUpload]
# xhr.readyState == 4 && xhr.status == 200时, xhr对象值
> onabort: ƒ (e)
> onerror: ƒ (e)
> onload: ƒ (e)
> onloadend: ƒ (e)
> onloadstart: ƒ (e)
> onprogress: ƒ (e)
> onreadystatechange: ƒ ()
> ontimeout: ƒ (e)
> readyState: 4
> response: {} # API接口返回的数据
> responseText: (...)
> responseType: "json"
> responseURL: "http://127.0.0.1:7901/get"
> responseXML: (...)
> status: 200
> statusText: "OK"
> timeout: 0
> withCredentials: false
> upload: XMLHttpRequestUpload {onloadstart: null, onprogress: null, onabort: null, onerror: null, onload: null, …}请求上传事件xhr.upload
# xhr.upload=[object XMLHttpRequestUpload]:上传阶段
const xhr = new XMLHttpRequest();
const upload = xhr['upload'];
for (let i in upload) {
console.log(i + '=' + upload[i]);
}
> onloadstart=null
> onprogress=null
> onabort=null
> onerror=null
> onload=null
> ontimeout=null
> onloadend=null
> addEventListener=function addEventListener() { [native code] }
> removeEventListener=function removeEventListener() { [native code] }
> dispatchEvent=function dispatchEvent() { [native code] }响应下载事件xhr
# 进程
> onloadstart=null
> onprogress=null
> onload=null
> onloadend=null
> ontimeout=null
> onabort=null
> onerror=null
> onreadystatechange=null
> addEventListener=function addEventListener() { [native code] }
> removeEventListener=function removeEventListener() { [native code] }
> dispatchEvent=function dispatchEvent() { [native code] }
# 请求成功时响应的事件
> onloadstart=null
> onprogress=null
> onload=null
> onloadend=null
# 网络异常
> readyState: 4
> response: "Not Found"
> responseText: "Not Found"
> responseType: ""
> responseXML: null
> status: 404
> statusText: "Not Found"
> timeout: 0xhr.onprogress
onprogress = function(e) {
if (e.lengthComputable) {
//
}
var current = e.loaded / e.total;
}xhr.onreadystatechange
# xhr.readyState, 状态值
> 0: 请求未初始化
> 1: 服务器连接已建立
> 2: 请求已接收
> 3: 请求处理中
> 4: 请求已完成,且响应已就绪
# xhr.status, HTTP response status codes
console.log(`xhr.readyState= ${xhr.readyState}`); # 0
console.log(`xhr.status= ${xhr.status}`); # 0
xhr.onreadystatechange = function () {
console.log(`xhr.readyState= ${xhr.readyState}`); # 依次为1 2 3 4
console.log(`xhr.status= ${xhr.status}`); # 依次为0 200 200 200
}xhr.open
# open(method,url,async)
> 规定请求的类型、URL 以及是否异步处理请求。
method:请求的类型;GET 或 POST
url:文件在服务器上的位置
async:true(异步)或 false(同步)xhr.responseType
# responseType 设置响应类型
--------------------------------------------------------------------
"" DOMString (这个是默认类型), 是一个UTF-16字符串
arraybuffer ArrayBuffer对象, 类型化数组, new ArrayBuffer() FileReader.readAsArrayBuffer() 从base64 字符串获取
blob Blob对象, 二进制大数据对象
document Document对象
json JS 对象 , 解析得到的从服务器返回来的JSON字符串
text DOMString
--------------------------------------------------------------------
# 当设置为responseType = "json"时,响返回的内容中,有两个字段不能读取responseText、responseXML
> responseText: (...)
> responseType: "json"
> responseXML: (...)
# 当设置为responseType不是json时
> response: "{"code":0,"msg":"success","data":{"body":{"account":"admin","password":"&&&&&&&"},"method":"POST"}}"
> responseText: "{"code":0,"msg":"success","data":{"body":{"account":"admin","password":"&&&&&&&"},"method":"POST"}}"
> responseType: ""
> responseXML: nullxhr.send
# xhr.send(string)的参数string可以是以下几种类型, 仅用于 POST 请求
> ArrayBuffer
> Blob
> Document
> DOMString
> FormData
> null完整示例
/**
* Author: 王军
* Email: ysungod@163.com
*
* import ajax from '/public/ajax.js';
* <script type="text/javascript" src="/public/ajax.js"></script>
ajax({
base: '',
path: '',
type: 'upload',
method: 'GET',
async: true,
timeout: 5000,
headers: {},
dataType: 'json',
responseType: 'json',
data: {},
uploadProgress: function() {},
complete: function() {},
success: function(res) {},
fail: function(err) {}
});
*/
(function(global, factory) {
if (typeof exports == 'object' && typeof module == 'object') {
module.exports = factory();
} else if (typeof define == 'function' && define.amd) {
define([], factory);
} else if (typeof exports == 'object') {
exports.ajax = factory;
} else {
(global = global || self, global.ajax = factory);
}
// typeof exports == 'object' && typeof module == 'object' ? module.exports = factory() :
// typeof define == 'function' && define.amd ? define([], factory) :
// typeof exports == 'object' ? exports.ajax = factory :
// (global = global || self, global.ajax = factory);
})(this, function(options) {'use strict';
const JSON_STRING = 'json';
options = options || {};
options.base = options.base || "";
options.path = options.path || "";
options.method = (options.method || 'GET').toUpperCase();
options.async = options.async || true;
options.timeout = options.timeout || 15000;
options.headers = options.headers || {};
//设置为"json"时,responseText与responseXML就不能读取,否则报错
options.dataType = options.dataType || JSON_STRING;
options.responseType = (options.responseType || 'json').toLowerCase();
options.data = options.data || {};
options.base = options.base.match(/\/$/g) == null ? (options.base + '/') : options.base;
//
var url = "";
if (options.path.match(/^(https?:\/\/)/g) != null) {
url = options.path;
} else {
options.path = options.path.match(/^\//g) == null ? options.path : options.path.replace(/^\//g, '');
url = options.base + options.path;
}
console.info(options);
/**
* XMLHttpRequest
*/
var xhr = null;
if (window.XMLHttpRequest) {
//IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
//IE5、IE6
xhr = new window.ActiveXObject("Microsoft.xhr");
} else {
throw new Error("Your brower don’t support XMLHttpRequest, please use Chrome to open it.");
}
//time out
var TIMEOUT = null;
if (options.responseType) {
xhr.responseType = options.responseType;
}
/**
* 上传进度设计
*
*/
xhr.upload.onprogress = function(e) {
let size = e.total;
let pros = e.loaded;
let scale = size/100;
let value = 0;
value= (pros/scale).toFixed(2);
if (pros >= size) {
value = 100;
}
options.uploadProgress && options.uploadProgress(value);
}
xhr.upload.onload = function(e) {
e['upload'] = e['type'];
// console.log(e);
}
xhr.upload.onloadend = function(e) {
e['upload'] = e['type'];
// console.log(e);
}
xhr.upload.ontimeout = function(e) {
e['upload'] = e['type'];
// console.log(e);
}
xhr.upload.onabort = function(e) {
e['upload'] = e['type'];
// console.log(e);
}
xhr.upload.onerror = function(e) {
e['upload'] = e['type'];
// console.log(e);
}
/**
* 下载进度
*
*/
xhr.onloadstart = function(e) {
// console.log(e);
}
xhr.onprogress = function(e) {
// console.log(e);
}
xhr.onload = function(e) {
// console.log(e);
}
xhr.onloadend = function(e) {
// console.log(e);
}
xhr.ontimeout = function(e) {
// console.log(e);
}
xhr.onabort = function(e) {
// console.log(e);
}
xhr.onerror = function(e) {
// console.log(e);
}
//request state
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
//complete
window.clearTimeout(TIMEOUT);
if (options.complete) {
options.complete();
}
if (xhr.status == 200) {
//success
if (options.success) {
console.log(xhr);
options.success(xhr.response);
}
return ;
}
//fail
if (options.fail) {
options.fail({state: xhr.status, msg: options.dataType !== JSON_STRING ? xhr.responseText : '消息通道出现异常错误'});
}
}
}
//转换数据为PATH参数
var transArguments = function(data) {
data = data || {};
var _path = "";
for (var i in data) {
_path += i + '=' + encodeURIComponent(data[i]) + '&';
}
_path = _path.replace(/&$/g, '');
return _path;
}
//GET
if (options.method == 'GET') {
var _url = url + '?' + transArguments(options.data);
_url = _url.replace(/(\&|\?)$/g, '');
xhr.open(options.method, _url, true);
for (var key in options.headers) {
xhr.setRequestHeader(key, options.headers[key]);
}
xhr.send(null);
}
//POST
if (options.method == 'POST') {
xhr.open(options.method, url, true);
//上传文件清空Content-type, 否则会异常
if (options.type !== 'upload') {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
}
for (var key in options.headers) {
xhr.setRequestHeader(key, options.headers[key]);
}
//上传文件
if (options.type === 'upload') {
xhr.send(options.data);
} else {
xhr.send(transArguments(options.data));
}
}
//超时
TIMEOUT = window.setTimeout(function () {
xhr.abort();
//完成
options.complete && options.complete({});
//失败
options.fail && options.fail({state: 408, msg: 'timeout'});
}, options.timeout);
});Ajax-hook
WebSocket
WebSocket 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。
代码示例
// Create socket connection.
const socket = new socket('ws://localhost:8080');
switch (socket.readyState) {
case socket.CONNECTING: // 值为0,表示正在连接。
// do something
break;
case socket.OPEN: // 值为1,表示连接成功,可以通信了。
// do something
break;
case socket.CLOSING: // 值为2,表示连接正在关闭。
// do something
break;
case socket.CLOSED: // 值为3,表示连接已经关闭,或者打开连接失败。
// do something
break;
default:
// this never happens
break;
}
// 发送数据
var data = new ArrayBuffer(10000000);
socket.send(data);
// socket.bufferedAmount:表示还有多少字节的二进制数据没有发送出去。
if (socket.bufferedAmount === 0) {
// 发送完毕
} else {
// 发送还没结束
}
// 事件:连接成功
socket.onopen = function(event) {
socket.send(data); // 对要传输的数据进行排队。
}
// 事件:接收数据
socket.onmessage = function(event) {
//
}
// 事件:连接错误
socket.onerror = function(event) {
//
}
// 事件:连接关闭
socket.onclose = function(event) {
//
}
socket.close([code[, reason]]); // 关闭当前链接。Socket.IO
Socket.IO 实现了实时双向的基于事件的通讯机制,旨在让各种浏览器与移动设备上实现实时 app 功能,模糊化各种传输机制。
代码示例
- 服务端
import { Server } from 'socket.io';
const io = new Server(3000);
io.on('connection', (socket) => {
// send a message
socket.emit('sendMsg', {});
// receive a message
socket.on('receiveMsg', (data) => {
console.log(data);
});
});- 客户端
import { io } from 'socket.io-client';
const socket = io('ws://localhost:3000');
// receive a message
socket.on('sendMsg', (data) => {
console.log(data);
});
// send a message
socket.emit('receiveMsg', {});WebRTC
WebRTC(Web Real-Time Communication),即网页即时通信,是一个支持网页浏览器进行实时语音对话或视频对话的API,可以在基于浏览器应用中实现任意点对点(peer-to-peer)的数据、音频、视频 或它们的任意组合的通信。
WebRTC 协议
- ICE:交互式连接设施Interactive Connectivity Establishment (ICE) 是一个允许你的浏览器和对端浏览器建立连接的协议框架。
- STUN:是一个允许位于 NAT 后的客户端找出自己的公网地址,判断出路由器阻止直连的限制方法的协议。
- NAT:网络地址转换协议Network Address Translation (NAT) 用来给你的(私网)设备映射一个公网的 IP 地址的协议。
- TURN:NAT 的中继穿越方式Traversal Using Relays around NAT (TURN) 通过 TURN 服务器中继所有数据的方式来绕过“对称型 NAT”。
- SDP:会话描述协议Session Description Protocol (SDP) 是一个描述多媒体连接内容的协议,例如分辨率,格式,编码,加密算法等。
adapter.js
adapter.js 是一个用于将应用程序与 WebRTC 中的规范更改和前缀差异综合的适配器。