EventSource 是浏览器端配合服务端事件(Server-Sent Events)使用的技术。和 WebSocket 相比,服务端事件是单向推送,而 WebSocket 是双向通信。
浏览器端
// 第二个参数是可选的,withCredentials 目前用于跨域 cookie,默认 false
const es = new EventSource(url, { withCredentials: true });
es.addEventListener('error', err => console.error(err));
// 监听 message 事件
es.addEventListener('message', evt => console.log(evt.data));
// 监听 type/custom 事件
es.addEventListener('type/custom', evt => console.log(evt.data));
EventSource 要求的是持续不断的输出,如果请求正常地结束终止了,那么客户端会再次自动发起连接,除非显式地调用 close()
方法来结束连接。
服务端
服务端的实现是返回标记为 Content-Type: text/event-stream
的持续内容,其内容的基本格式是:
event: evt-type
data: any message
其中,event
对应 EventSource 监听的事件类型,但该内容是可选的,如果没指定 event,那么则被 message
事件监听(可简化为 es.onmessage
)。
紧邻的 data
字段会把内容合并起来,连接内容是一个空行。
直接以 :
开头的内容则是注释,不被解析使用。
示例
// EventSource
document.cookie = 'PHPSESSID="";max-age=-1'
const list = document.getElementById('J-list');
function addMsg(type, data) {
data = data || '';
data = data.replace(/\n/g, '<br />');
constele = document.createElement('li');
ele.innerHTML = type ? `<span class="type">{type}</span>{data}` : data;ele.classList.add(`type-{type}`);list.appendChild(ele);ele.scrollIntoView();
}
const es = new EventSource('msg.php');
es.onerror = err => console.error(err);
es.onmessage = e => {
addMsg(e.type, e.data);
};
es.addEventListener('ping', e => {
addMsg('ping', e.data);
});
es.addEventListener('start', e => {
addMsg('start', e.data);
});
es.addEventListener('times', e => {
addMsg('times', e.data);
if (e.data >=5 ) {
es.close();
addMsg('close');
}
});
window.addEventListener('unload', () => {
es.close();
});
<!-- Server-send Event -->
<?php
if (!session_id()) session_start();
if (!isset(_SESSION['count'])) {_SESSION['count'] = 1;
} else if(_SESSION['count']>5) {
session_unset();_SESSION['count'] = 1;
} else {
_SESSION['count'] += 1;
}
// Send appropriate mime type
header("Content-Type: text/event-stream\n\n");
echo "event: times\n";
echo "data: " ._SESSION['count'];
echo "\n\n";
// Read sample messages
filename = "data.txt";handle = fopen(filename, "r");contents = fread(handle, filesize(filename));
fclose(handle);
// Split the messages into an arraymessages = preg_split( "/\n\n/", contents );
// Send one message every 2 to 7 seconds
foreach (messages as message ) {
echomessage;
echo "\n\n";
ob_flush();
flush();
sleep( rand(0, 2) );
}
?>