做偏门网站,阿里云最低服务器可以做几个网站,推广兼职,软件工程培训班出来好就业吗虽然网上的都是用sse实现将实时消息流不间断的推给前端#xff0c;但是sse也可以模拟websocket进行突发的消息通知#xff0c;而不是一直读取数据并返回数据。即服务端保存所有的连接对象#xff0c;前端管理界面发送正常的http请求#xff0c;在后端遍历所有的连接对象但是sse也可以模拟websocket进行突发的消息通知而不是一直读取数据并返回数据。即服务端保存所有的连接对象前端管理界面发送正常的http请求在后端遍历所有的连接对象将消息广播。就可以实现一种类似双向通讯的形式了。
代码参考了Server-side Events (SSE) : A deep dive into client-server architecture | Implementation in Golang在这基础上实现了房间机制房间ID由前端生成并传递鉴权机制请自行通过token中间件等形式实现。
package mainimport (fmtnet/httpgithub.com/gin-gonic/gin
)// 房间号为keyclient数组为value
var clients make(map[string][]chan string)// 广播房间内的所有用户
func broadcast(roomID string, data string) {for _, client : range clients[roomID] {client - data}
}
//配置跨域
func configCors() gin.HandlerFunc {return func(c *gin.Context) {method : c.Request.Methodc.Header(Access-Control-Allow-Origin, *)c.Header(Access-Control-Allow-Methods, POST, GET, OPTIONS, PUT, DELETE, UPDATE)c.Header(Access-Control-Allow-Headers, *)c.Header(Access-Control-Expose-Headers, Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type)c.Header(Access-Control-Allow-Credentials, true)//放行所有OPTIONS方法if method OPTIONS {c.AbortWithStatus(http.StatusNoContent)}// 处理请求c.Next()}
}//前端初始化时连接该接口
func connect(c *gin.Context) {roomID : c.Param(id)// Set the response header to indicate SSE content typec.Header(Content-Type, text/event-stream)c.Header(Cache-Control, no-cache)c.Header(Connection, keep-alive)// Create a channel to send events to the clientprintln(Client connected)eventChan : make(chan string)if clients[roomID] nil {clients[roomID] []chan string{}}clients[roomID] append(clients[roomID], eventChan) // Add the client to the clients mapdefer func() {// 删除该房间的该用户按值删除数组元素for _, v : range clients[roomID] {if v ! eventChan {clients[roomID] append(clients[roomID], v)}}close(eventChan)}()// Listen for client close and remove the client from the listnotify : c.Writer.CloseNotify()go func() {-notifyfmt.Println(Client disconnected)}()// Continuously send data to the clientfor {data : -eventChanprintln(Sending data to client, data)fmt.Fprintf(c.Writer, data: %s\n\n, data)c.Writer.Flush()}
}// 发送消息接口
func sendMsg(c *gin.Context) {// data : c.PostForm(data)roomID : c.Param(id)data : c.DefaultQuery(name, urlyy)// print data to consoleprintln(Data received from client :, data)broadcast(roomID, data)c.JSON(http.StatusOK, gin.H{message: Data sent to clients})
}func main() {router : gin.Default()router.Use(configCors())// SSE endpoint that the clients will be listening torouter.GET(/sse/:id, connect)// Handle POST requestrouter.GET(/send/:id, sendMsg)// Start the servererr : router.Run(:6666)if err ! nil {fmt.Println(err)}
}前端代码
!DOCTYPE html
html langenheadmeta charsetUTF-8titleSSE Client/title
/headbodyh1SSE Client/h1div idsse-data/divscriptconst sseDataElement document.getElementById(sse-data);// Create an EventSource to listen to the /sse endpoint// 注意这里多加了一个路径属性就是房间ID// 测试时可以另建一个html文件将它的房间ID更改成不一样的const eventSource new EventSource(http://localhost:6666/sse/1);// Event listener for messages received from the servereventSource.onmessage function (event) {const data event.data;appendDataToDiv(data);};// Event listener for SSE errorseventSource.onerror function (event) {console.error(SSE Error:, event);};// Function to append data to the SSE data divfunction appendDataToDiv(data) {const p document.createElement(p);p.textContent data;sseDataElement.appendChild(p);}/script
/body/html发送消息的接口 http://127.0.0.1:8587/send/1?name1234name不传则默认为urlyy。调用该接口会将消息1234发给1号房间的所有用户