开发 Web 应用程序使用 服务器发送事件 is straightforward. You'll need a bit of code on the server to stream events to the front-end, but the client side code works almost identically to websockets in part of handling incoming events. This is one-way connection, so you can't send events from a client to a server.
The server-sent event API is contained in the
EventSource
interface; to open a connection to the server to begin receiving events from it, create a new
EventSource
object with the URL of a script that generates the events. For example:
const evtSource = new EventSource("ssedemo.php");
If the event generator script is hosted on a different origin, a new
EventSource
object should be created with both the URL and an options dictionary. For example, assuming the client script is on
example.com
:
const evtSource = new EventSource("//api.example.com/ssedemo.php", { withCredentials: true } );
Once you've instantiated your event source, you can begin listening for messages from the server by attaching a handler for the
message
event:
evtSource.onmessage = function(event) {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");
newElement.innerHTML = "message: " + event.data;
eventList.appendChild(newElement);
}
This code listens for incoming messages (that is, notices from the server that do not have an
event
field on them) and appends the message text to a list in the document's HTML.
You can also listen for events with
addEventListener()
:
evtSource.addEventListener("ping", function(event) {
const newElement = document.createElement("li");
const time = JSON.parse(event.data).time;
newElement.innerHTML = "ping at " + time;
eventList.appendChild(newElement);
});
This code is similar, except that it will be called automatically whenever the server sends a message with the
event
field set to "ping"; it then parses the JSON in the
data
field and outputs that information.
当
not used over HTTP/2
, SSE suffers from a limitation to the maximum number of open connections, which can be especially painful when opening multiple tabs, as the limit is
per browser
and is set to a very low number (6). The issue has been marked as "Won't fix" in
Chrome
and
Firefox
. This limit is per browser + domain, which means that you can open 6 SSE connections across all of the tabs to
www.example1.com
and another 6 SSE connections to
www.example2.com
(per
Stackoverflow
). When using HTTP/2, the maximum number of simultaneous
HTTP streams
is negotiated between the server and the client (defaults to 100).
The server-side script that sends events needs to respond using the MIME type
text/event-stream
. Each notification is sent as a block of text terminated by a pair of newlines. For details on the format of the event stream, see
事件流格式
.
PHP code for the example we're using here follows:
date_default_timezone_set("America/New_York");
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");
$counter = rand(1, 10);
while (true) {
// Every second, send a "ping" event.
echo "event: ping\n";
$curDate = date(DATE_ISO8601);
echo 'data: {"time": "' . $curDate . '"}';
echo "\n\n";
// Send a simple message at random intervals.
$counter--;
if (!$counter) {
echo 'data: This is a message at time ' . $curDate . "\n\n";
$counter = rand(1, 10);
}
ob_end_flush();
flush();
sleep(1);
}
The code above generates an event every second, with the event type "ping". Each event's data is a JSON object containing the ISO 8601 timestamp corresponding to the time at which the event was generated. At random intervals, a simple message (with no event type) is sent.
注意 : You can find a full example that uses the code shown in this article on GitHub — see Simple SSE demo using PHP.
When problems occur (such as a network timeout or issues pertaining to
access control
), an error event is generated. You can take action on this programmatically by implementing the
onerror
callback on the
EventSource
对象:
evtSource.onerror = function(err) {
console.error("EventSource failed:", err);
};
By default, if the connection between the client and server closes, the connection is restarted. The connection is terminated with the
.close()
方法。
evtSource.close();
The event stream is a simple stream of text data which must be encoded using UTF-8 . Messages in the event stream are separated by a pair of newline characters. A colon as the first character of a line is in essence a comment, and is ignored.
注意: The comment line can be used to prevent connections from timing out; a server can send a comment periodically to keep the connection alive.Each message consists of one or more lines of text listing the fields for that message. Each field is represented by the field name, followed by a colon, followed by the text data for that field's value.
Each message received has some combination of the following fields, one per line:
event
addEventListener()
to listen for named events. The
onmessage
handler is called if no event name is specified for a message.
data
EventSource
receives multiple consecutive lines that begin with
data:
,
it concatenates them
, inserting a newline character between each one. Trailing newlines are removed.
id
EventSource
object's last event ID value.
retry
The reconnection time to use when attempting to send the event. This must be an integer, specifying the reconnection time in milliseconds. If a non-integer value is specified, the field is ignored.
All other field names are ignored.
注意: If a line doesn't contain a colon, the entire line is treated as the field name with an empty value string.In the following example, there are three messages sent. The first is just a comment, since it starts with a colon character. As mentioned previously, this can be useful as a keep-alive mechanism if messages might not be sent regularly.
The second message contains a data field with the value "some text". The third message contains a data field with the value "another message\nwith two lines". Note the newline special character in the value.
: this is a test stream data: some text data: another message data: with two lines
This example sends named events. Each has an event name specified by the
event
field, and a
data
field whose value is an appropriate JSON string with the data needed for the client to act on the event. The
data
field could, of course, have any string data; it doesn't have to be JSON.
event: userconnect
data: {"username": "bobby", "time": "02:33:48"}
event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}
event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}
You don't have to use just unnamed messages or typed events; you can mix them together in a single event stream.
event: userconnect
data: {"username": "bobby", "time": "02:33:48"}
data: Here's a system message of some kind that will get used
data: to accomplish some task.
event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
EventSource
| 桌面 | 移动 | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
EventSource
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera 11 | Safari 5 | WebView Android ≤37 | Chrome Android 18 | Firefox Android 45 | Opera Android 11 | Safari iOS 5 | Samsung Internet Android 1.0 |
EventSource()
构造函数
|
Chrome 9 | Edge 79 | Firefox 6 | IE 不支持 No | Opera 11 | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
close
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
error
event
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
message
event
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
onerror
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
onmessage
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
onopen
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
open
event
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
readyState
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
url
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
withCredentials
|
Chrome 6 | Edge 79 | Firefox 6 | IE 不支持 No | Opera Yes | Safari 5 | WebView Android Yes | Chrome Android 18 | Firefox Android 45 | Opera Android 12 | Safari iOS 5 | Samsung Internet Android 1.0 |
| Available in workers | Chrome Yes | Edge 79 | Firefox 53 | IE 不支持 No | Opera Yes | Safari Yes | WebView Android Yes | Chrome Android Yes | Firefox Android 53 | Opera Android Yes | Safari iOS Yes | Samsung Internet Android Yes |
完整支持
不支持