京東商城網(wǎng)站設(shè)計(jì)酒店如何進(jìn)行網(wǎng)絡(luò)營(yíng)銷
HTML5 Server-Sent Events (SSE) 是一種技術(shù),它允許服務(wù)器向?yàn)g覽器推送更新。與傳統(tǒng)的輪詢不同,SSE提供了真正的單向?qū)崟r(shí)通信通道:服務(wù)器可以主動(dòng)發(fā)送數(shù)據(jù)到客戶端,而不需要客戶端發(fā)起請(qǐng)求。這對(duì)于實(shí)現(xiàn)實(shí)時(shí)更新的應(yīng)用非常有用,比如股票行情、社交網(wǎng)絡(luò)更新、聊天應(yīng)用等。
SSE 的特點(diǎn)
- 單向通信:服務(wù)器到客戶端的通信是單向的,客戶端不能通過(guò)同一個(gè)連接向服務(wù)器發(fā)送信息。
- 自動(dòng)重連:如果連接斷開(kāi),瀏覽器會(huì)自動(dòng)嘗試重新建立連接。
- 簡(jiǎn)單性:相比WebSocket,SSE更簡(jiǎn)單,因?yàn)樗贖TTP協(xié)議,并且不需要額外的握手過(guò)程。
- 事件驅(qū)動(dòng):服務(wù)器可以通過(guò)發(fā)送特定格式的消息來(lái)觸發(fā)客戶端上的事件處理函數(shù)。
SSE 的基本語(yǔ)法
服務(wù)器端
服務(wù)器需要設(shè)置正確的HTTP響應(yīng)頭,并使用特定的格式發(fā)送消息給客戶端:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alivedata: This is a message\n\n
每個(gè)消息以data:
開(kāi)頭,后面跟著消息內(nèi)容,最后用兩個(gè)換行符(\n\n
)結(jié)束。此外,還可以發(fā)送其他類型的指令,如event:
指定事件類型或id:
設(shè)置事件ID。
客戶端
在客戶端,使用JavaScript中的EventSource
對(duì)象來(lái)接收來(lái)自服務(wù)器的消息:
if(typeof(EventSource)!=="undefined") {var source = new EventSource("/events");source.onmessage = function(event) {console.log("New message:", event.data);};source.onerror = function(event) {console.error("Error occurred:", event);};
} else {console.log("Your browser does not support server-sent events.");
}
示例:
下面是五個(gè)使用HTML5 Server-Sent Events (SSE) 從Gin框架中獲取信息的完整示例,每個(gè)示例展示了不同的應(yīng)用場(chǎng)景。這些示例包括了從簡(jiǎn)單的計(jì)數(shù)器更新到更復(fù)雜的實(shí)時(shí)通知系統(tǒng)和動(dòng)態(tài)數(shù)據(jù)推送。
示例1: 簡(jiǎn)單計(jì)數(shù)器更新
服務(wù)器端(Go + Gin)代碼:
文件:main.go
package mainimport ("fmt""net/http""time""github.com/gin-gonic/gin"
)func main() {r := gin.Default()// 加載HTML模板文件夾中的所有HTML文件,以便可以在響應(yīng)中使用這些模板。// 這里假設(shè)HTML文件位于項(xiàng)目根目錄下的"templates"文件夾中。r.LoadHTMLGlob("templates/*")// 定義根路徑"/"的GET請(qǐng)求處理器。// 當(dāng)用戶訪問(wèn)根URL時(shí),將渲染并返回"index.html"模板。r.GET("/", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", nil)})// 定義處理SSE事件的GET請(qǐng)求處理器,路徑為"/events"。r.GET("/events", func(c *gin.Context) {// 設(shè)置HTTP響應(yīng)頭以支持SSE。// Content-Type設(shè)置為"text/event-stream"表示這是一個(gè)SSE流。// Cache-Control設(shè)置為"no-cache"防止瀏覽器緩存數(shù)據(jù)。// Connection設(shè)置為"keep-alive"保持連接打開(kāi)。c.Header("Content-Type", "text/event-stream")c.Header("Cache-Control", "no-cache")c.Header("Connection", "keep-alive")// 檢查ResponseWriter是否實(shí)現(xiàn)了Flusher接口,確??梢詫?shí)時(shí)發(fā)送數(shù)據(jù)。flusher, ok := c.Writer.(http.Flusher)if !ok {http.Error(c.Writer, "Streaming unsupported!", http.StatusInternalServerError)return}// 開(kāi)始一個(gè)循環(huán)來(lái)模擬服務(wù)器向客戶端發(fā)送20條消息。for i := 0; i < 20; i++ { // 發(fā)送20條消息// 構(gòu)建要發(fā)送的消息,格式為"data: [message]\n\n"。message := fmt.Sprintf("data: %d\n\n", i)c.Writer.WriteString(message)// 調(diào)用Flush()方法立即將緩沖區(qū)的數(shù)據(jù)發(fā)送給客戶端。flusher.Flush()// 每次發(fā)送消息后暫停1秒,模擬延遲。time.Sleep(time.Second)// 如果客戶端斷開(kāi)了連接(例如關(guān)閉了頁(yè)面),則退出循環(huán)。if c.Writer.Size() == 0 {break}}})// 啟動(dòng)HTTP服務(wù)器,監(jiān)聽(tīng)8080端口。// Gin框架默認(rèn)在0.0.0.0上監(jiān)聽(tīng),意味著可以從任何網(wǎng)絡(luò)接口訪問(wèn)該服務(wù)。r.Run(":8080") // 監(jiān)聽(tīng)并在 0.0.0.0:8080 上啟動(dòng)服務(wù)
}
客戶端(HTML + JavaScript)代碼:
文件:templates/index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><!-- 設(shè)置文檔的字符編碼為UTF-8 --><title>SSE Counter</title><!-- 設(shè)置頁(yè)面標(biāo)題 -->
</head>
<body><h1>Counter:</h1><!-- 顯示計(jì)數(shù)器標(biāo)題 --><p id="counter">0</p><!-- 顯示計(jì)數(shù)器數(shù)值,初始值設(shè)置為0 --><script type="text/javascript">// 檢查瀏覽器是否支持Server-Sent Events (SSE)if(typeof(EventSource)!=="undefined") {// 創(chuàng)建一個(gè)新的EventSource對(duì)象,連接到服務(wù)器端點(diǎn)"/events"var source = new EventSource("/events");// 當(dāng)從服務(wù)器接收到消息時(shí)觸發(fā)此函數(shù)source.onmessage = function(event) {// 更新頁(yè)面中ID為"counter"的<p>元素的內(nèi)容為接收到的消息數(shù)據(jù)document.getElementById("counter").innerHTML = event.data;};// 如果發(fā)生錯(cuò)誤(例如連接斷開(kāi)),觸發(fā)此函數(shù)source.onerror = function(event) {console.error("Error occurred:", event);// 可選:關(guān)閉事件源以防止進(jìn)一步嘗試重新連接// source.close();};} else {// 如果瀏覽器不支持SSE,則顯示提示信息document.getElementById("counter").innerHTML = "Sorry, your browser does not support server-sent events.";}</script><!-- 該腳本塊用于與服務(wù)器建立SSE連接,并根據(jù)接收的數(shù)據(jù)更新頁(yè)面上的計(jì)數(shù)器 -->
</body>
</html>
示例2: 實(shí)時(shí)通知系統(tǒng)
服務(wù)器端(Go + Gin)代碼:
文件:main.go
package mainimport ("fmt""net/http""sync""time""github.com/gin-gonic/gin"
)// 定義一個(gè)互斥鎖,用于在廣播通知時(shí)同步對(duì)clients的訪問(wèn)。
var mu sync.Mutex// clients是一個(gè)保存所有客戶端連接的映射,鍵是客戶端的通信信道,值是一個(gè)布爾值表示狀態(tài)。
var clients = make(map[chan string]bool)// broadcastNotification向所有連接的客戶端廣播通知消息。
func broadcastNotification(msg string) {// 加鎖以確保并發(fā)安全。mu.Lock()defer mu.Unlock()// 遍歷所有客戶端,向它們發(fā)送消息。for client := range clients {client <- msg}
}// handleEvents處理客戶端的事件流請(qǐng)求,保持連接開(kāi)放并實(shí)時(shí)發(fā)送事件數(shù)據(jù)。
func handleEvents(c *gin.Context) {// 設(shè)置響應(yīng)頭,指明這是一個(gè)事件流。c.Header("Content-Type", "text/event-stream")c.Header("Cache-Control", "no-cache")c.Header("Connection", "keep-alive")// 創(chuàng)建一個(gè)信道,用于向該客戶端發(fā)送消息。clientChan := make(chan string)defer close(clientChan)// 加鎖以確保并發(fā)安全。mu.Lock()clients[clientChan] = truemu.Unlock()// 檢查是否支持Flush操作。flusher, ok := c.Writer.(http.Flusher)if !ok {http.Error(c.Writer, "Streaming unsupported!", http.StatusInternalServerError)return}// 啟動(dòng)一個(gè)goroutine監(jiān)聽(tīng)clientChan,并向客戶端發(fā)送數(shù)據(jù)。go func() {for msg := range clientChan {c.Writer.WriteString(fmt.Sprintf("data: %s\n\n", msg))flusher.Flush()}}()// 保持連接開(kāi)放,定期檢查客戶端是否還在線。for {time.Sleep(time.Second)if _, err := c.Writer.Write([]byte{}); err != nil {delete(clients, clientChan)break}}
}// main是程序的入口點(diǎn),初始化Gin框架并設(shè)置路由。
func main() {r := gin.Default()r.LoadHTMLGlob("templates/*")// 定義根路徑"/"的GET請(qǐng)求處理器。// 當(dāng)用戶訪問(wèn)根URL時(shí),將渲染并返回"index.html"模板。r.GET("/", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", nil)})// 定義"/events"的GET請(qǐng)求處理器,用于處理事件流。r.GET("/events", handleEvents)// 啟動(dòng)一個(gè)goroutine定期生成通知消息并廣播。go func() {for i := 0; ; i++ {time.Sleep(3 * time.Second)broadcastNotification(fmt.Sprintf("New Notification %d", i))}}()// 啟動(dòng)HTTP服務(wù)器,監(jiān)聽(tīng)8080端口。r.Run(":8080")
}
客戶端(HTML + JavaScript)代碼:
文件:templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 定義文檔的字符編碼 -->
<meta charset="UTF-8">
<!-- 設(shè)置網(wǎng)頁(yè)的標(biāo)題 -->
<title>Real-time Notifications</title>
</head>
<body>
<!-- 顯示通知列表的標(biāo)題 -->
<h1>Notifications:</h1>
<!-- 動(dòng)態(tài)添加通知的容器 -->
<ul id="notifications"></ul><script type="text/javascript">
// 檢查瀏覽器是否支持EventSource接口
if(typeof(EventSource)!=="undefined") {// 創(chuàng)建EventSource對(duì)象,連接到服務(wù)器的事件流var source = new EventSource("/events");// 監(jiān)聽(tīng)來(lái)自服務(wù)器的消息事件source.onmessage = function(event) {// 創(chuàng)建一個(gè)新的<li>元素來(lái)顯示通知var li = document.createElement("li");// 設(shè)置<li>元素的文本內(nèi)容為接收到的消息數(shù)據(jù)li.textContent = event.data;// 將新的通知添加到通知列表中document.getElementById("notifications").appendChild(li);};
} else {// 如果瀏覽器不支持EventSource,顯示提示信息document.write("Sorry, your browser does not support server-sent events.");
}
</script>
</body>
</html>
示例3: 動(dòng)態(tài)天氣更新
服務(wù)器端(Go + Gin)代碼:
文件:main.go
package mainimport ("fmt""net/http""time""github.com/gin-gonic/gin"
)// getWeatherData 獲取天氣數(shù)據(jù)。在實(shí)際應(yīng)用中,這個(gè)函數(shù)應(yīng)該與天氣服務(wù)API交互以獲取實(shí)時(shí)數(shù)據(jù)。
// 返回值: 一個(gè)描述天氣的字符串。
func getWeatherData() string {// 這里應(yīng)該有一個(gè)函數(shù)來(lái)獲取實(shí)際的天氣數(shù)據(jù)return "Sunny with a chance of meatballs."
}// main 是程序的入口點(diǎn)。它設(shè)置了一個(gè)Gin路由器,配置了兩個(gè)GET請(qǐng)求的處理器,并啟動(dòng)了服務(wù)器。
func main() {r := gin.Default()r.LoadHTMLGlob("templates/*")// 定義根路徑"/"的GET請(qǐng)求處理器。// 當(dāng)用戶訪問(wèn)根URL時(shí),將渲染并返回"index.html"模板。r.GET("/", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", nil)})// 定義"/weather"路徑的GET請(qǐng)求處理器。// 該處理器通過(guò)Server-Sent Events (SSE)每5分鐘向客戶端發(fā)送一次天氣更新。r.GET("/weather", func(c *gin.Context) {c.Header("Content-Type", "text/event-stream")c.Header("Cache-Control", "no-cache")c.Header("Connection", "keep-alive")flusher, ok := c.Writer.(http.Flusher)if !ok {http.Error(c.Writer, "Streaming unsupported!", http.StatusInternalServerError)return}for {data := getWeatherData()c.Writer.WriteString(fmt.Sprintf("data: %s\n\n", data))flusher.Flush()time.Sleep(5 * time.Minute) // 每五分鐘更新一次if c.Writer.Size() == 0 {break}}})// 啟動(dòng)HTTP服務(wù)器,監(jiān)聽(tīng)8080端口。r.Run(":8080")
}
客戶端(HTML + JavaScript)代碼:
文件:templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dynamic Weather Updates</title>
</head>
<body>
<h1>Weather:</h1>
<p id="weather">Loading...</p><script type="text/javascript">
if(typeof(EventSource)!=="undefined") {var source = new EventSource("/weather");source.onmessage = function(event) {document.getElementById("weather").innerHTML = event.data;};
} else {document.getElementById("weather").innerHTML = "Your browser does not support server-sent events.";
}
</script>
</body>
</html>
示例4: 聊天室消息推送
服務(wù)器端(Go + Gin)代碼:
文件:main.go
package mainimport ("fmt""net/http""sync""time""github.com/gin-gonic/gin"
)// messages 存儲(chǔ)聊天消息。
var messages []string// mu 用于保護(hù) messages 切片的并發(fā)訪問(wèn)安全。
var mu sync.Mutex// addMessage 向消息列表中添加新消息。
// 該函數(shù)通過(guò) mu 鎖確保并發(fā)安全。
func addMessage(msg string) {mu.Lock()defer mu.Unlock()messages = append(messages, msg)
}func main() {r := gin.Default()r.LoadHTMLGlob("templates/*")// 定義根路徑"/"的GET請(qǐng)求處理器。// 當(dāng)用戶訪問(wèn)根URL時(shí),將渲染并返回"index.html"模板。r.GET("/", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", nil)})// 處理"/chat"的GET請(qǐng)求,以Server-Sent Events(SSE)形式發(fā)送聊天消息。// 該處理器持續(xù)運(yùn)行,每秒向客戶端推送一次消息,直到連接關(guān)閉。r.GET("/chat", func(c *gin.Context) {c.Header("Content-Type", "text/event-stream")c.Header("Cache-Control", "no-cache")c.Header("Connection", "keep-alive")flusher, ok := c.Writer.(http.Flusher)if !ok {http.Error(c.Writer, "Streaming unsupported!", http.StatusInternalServerError)return}for {mu.Lock()msgs := messagesmu.Unlock()for _, msg := range msgs {c.Writer.WriteString(fmt.Sprintf("data: %s\n\n", msg))flusher.Flush()}time.Sleep(time.Second)if c.Writer.Size() == 0 {break}}})// 處理"/send"的POST請(qǐng)求,用于接收客戶端發(fā)送的新消息。// 從請(qǐng)求中提取"message"字段,并將其添加到消息列表中。r.POST("/send", func(c *gin.Context) {msg := c.PostForm("message")addMessage(msg)})// 啟動(dòng)HTTP服務(wù)器,監(jiān)聽(tīng)端口為8080。r.Run(":8080")
}
客戶端(HTML + JavaScript)代碼:
文件:templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chat Room</title>
</head>
<body>
<h1>Chat Room:</h1>
<ul id="messages"></ul>
<form id="chat-form"><input type="text" name="message" placeholder="Type a message..." required /><button type="submit">Send</button>
</form><script type="text/javascript">
// 檢查瀏覽器是否支持EventSource
if(typeof(EventSource)!=="undefined") {// 創(chuàng)建EventSource實(shí)例,建立到服務(wù)器的連接var source = new EventSource("/chat");// 監(jiān)聽(tīng)服務(wù)器發(fā)送的消息事件source.onmessage = function(event) {// 創(chuàng)建一個(gè)新的li元素var li = document.createElement("li");// 將服務(wù)器發(fā)送的數(shù)據(jù)設(shè)置為li元素的文本內(nèi)容li.textContent = event.data;// 將li元素添加到頁(yè)面上的messages元素中document.getElementById("messages").appendChild(li);};// 監(jiān)聽(tīng)表單的提交事件document.getElementById("chat-form").addEventListener("submit", function(event) {// 阻止默認(rèn)的表單提交行為event.preventDefault();// 創(chuàng)建FormData實(shí)例,用于處理表單數(shù)據(jù)var formData = new FormData(event.target);// 使用fetch API發(fā)送POST請(qǐng)求到服務(wù)器fetch('/send', {method: 'POST',body: formData,}).then(response => {// 如果服務(wù)器響應(yīng)成功,重置表單if (response.ok) {event.target.reset();}});});
} else {// 如果瀏覽器不支持EventSource,顯示提示信息document.write("Sorry, your browser does not support server-sent events.");
}
</script>
</body>
</html>
示例5: 實(shí)時(shí)股票價(jià)格更新
服務(wù)器端(Go + Gin)代碼:
文件:main.go
package mainimport ("fmt""math/rand""net/http""time""github.com/gin-gonic/gin"
)// getRandomStockPrice 生成一個(gè)隨機(jī)的股票價(jià)格。
// 假設(shè)股票價(jià)格在100到200之間。
func getRandomStockPrice() float64 {return rand.Float64()*100 + 100
}func main() {r := gin.Default()r.LoadHTMLGlob("templates/*")// 定義根路徑"/"的GET請(qǐng)求處理器。// 當(dāng)用戶訪問(wèn)根URL時(shí),將渲染并返回"index.html"模板。r.GET("/", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", nil)})// 處理"/stock"的GET請(qǐng)求,通過(guò)Server-Sent Events (SSE) 實(shí)時(shí)推送股票價(jià)格。r.GET("/stock", func(c *gin.Context) {c.Header("Content-Type", "text/event-stream")c.Header("Cache-Control", "no-cache")c.Header("Connection", "keep-alive")// 檢查響應(yīng)寫(xiě)入器是否支持flush操作。flusher, ok := c.Writer.(http.Flusher)if !ok {http.Error(c.Writer, "Streaming unsupported!", http.StatusInternalServerError)return}// 創(chuàng)建一個(gè)定時(shí)器,每10秒發(fā)送一次股票價(jià)格。ticker := time.NewTicker(10 * time.Second)defer ticker.Stop()for {select {case <-ticker.C:// 生成新的股票價(jià)格并發(fā)送到客戶端。price := getRandomStockPrice()c.Writer.WriteString(fmt.Sprintf("data: %.2f\n\n", price))flusher.Flush()// 如果響應(yīng)內(nèi)容為空,則終止循環(huán)。if c.Writer.Size() == 0 {return}}}})// 啟動(dòng)HTTP服務(wù)器,監(jiān)聽(tīng)端口8080。r.Run(":8080")
}
客戶端(HTML + JavaScript)代碼:
文件:templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Live Stock Price</title>
</head>
<body>
<h1>Stock Price:</h1>
<p id="price">Loading...</p><script type="text/javascript">
if(typeof(EventSource)!=="undefined") {var source = new EventSource("/stock");source.onmessage = function(event) {document.getElementById("price").innerHTML = "$" + event.data;};
} else {document.getElementById("price").innerHTML = "Your browser does not support server-sent events.";
}
</script>
</body>
</html>
這些示例涵蓋了從簡(jiǎn)單到復(fù)雜的不同SSE應(yīng)用場(chǎng)景。你可以根據(jù)自己的需求調(diào)整和擴(kuò)展這些例子,以適應(yīng)特定的業(yè)務(wù)邏輯或功能要求。
測(cè)試代碼下載
html5_api代碼