Go語言中websocket的使用demo分享
更新時間:2022年12月28日 10:45:45 作者:用戶6512516549724
WebSocket是一種在單個TCP連接上進(jìn)行全雙工通信的協(xié)議。這篇文章主要和大家分享了一個Go語言中websocket的使用demo,需要的可以參考一下
服務(wù)端代碼 main.go
package main
import (
"errors"
"fmt"
"net/http"
"sync"
"time"
"github.com/gorilla/websocket"
)
// http升級websocket協(xié)議的配置
var wsUpgrader = websocket.Upgrader{
// 允許所有CORS跨域請求
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 客戶端讀寫消息
type wsMessage struct {
messageType int
data []byte
}
// 客戶端連接
type wsConnection struct {
wsSocket *websocket.Conn // 底層websocket
inChan chan *wsMessage // 讀隊列
outChan chan *wsMessage // 寫隊列
mutex sync.Mutex // 避免重復(fù)關(guān)閉管道
isClosed bool
closeChan chan byte // 關(guān)閉通知
}
func (wsConn *wsConnection) wsReadLoop() {
for {
// 讀一個message
msgType, data, err := wsConn.wsSocket.ReadMessage()
if err != nil {
goto error
}
req := &wsMessage{
msgType,
data,
}
// 放入請求隊列
select {
case wsConn.inChan <- req:
case <-wsConn.closeChan:
goto closed
}
}
error:
wsConn.wsClose()
closed:
}
func (wsConn *wsConnection) wsWriteLoop() {
for {
select {
// 取一個應(yīng)答
case msg := <-wsConn.outChan:
// 寫給websocket
if err := wsConn.wsSocket.WriteMessage(msg.messageType, msg.data); err != nil {
goto error
}
case <-wsConn.closeChan:
goto closed
}
}
error:
wsConn.wsClose()
closed:
}
func (wsConn *wsConnection) procLoop() {
// 啟動一個gouroutine發(fā)送心跳
go func() {
for { //不斷向客戶端寫數(shù)據(jù),其實沒有它也是一樣的,客戶端可以檢測到斷開
time.Sleep(2 * time.Second)
if err := wsConn.wsWrite(websocket.TextMessage, []byte("heartbeat from server")); err != nil {
fmt.Println("heartbeat fail")
wsConn.wsClose()
break
}
}
}()
// 這是一個同步處理模型(只是一個例子),如果希望并行處理可以每個請求一個gorutine,注意控制并發(fā)goroutine的數(shù)量!!!
for {
msg, err := wsConn.wsRead()
if err != nil {
fmt.Println("read fail")
break
}
fmt.Println(string(msg.data))
err = wsConn.wsWrite(msg.messageType, msg.data) // 讀到數(shù)據(jù)后,同步的去寫數(shù)據(jù)。應(yīng)該寫成異步
if err != nil {
fmt.Println("write fail")
break
}
}
}
func wsHandler(resp http.ResponseWriter, req *http.Request) {
// 應(yīng)答客戶端告知升級連接為websocket
wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
if err != nil {
return
}
wsConn := &wsConnection{
wsSocket: wsSocket,
inChan: make(chan *wsMessage, 1000),
outChan: make(chan *wsMessage, 1000),
closeChan: make(chan byte),
isClosed: false,
}
// 處理器
go wsConn.procLoop()
// 讀協(xié)程
go wsConn.wsReadLoop()
// 寫協(xié)程
go wsConn.wsWriteLoop()
}
func (wsConn *wsConnection) wsWrite(messageType int, data []byte) error {
select {
case wsConn.outChan <- &wsMessage{messageType, data}:
case <-wsConn.closeChan:
return errors.New("websocket closed")
}
return nil
}
func (wsConn *wsConnection) wsRead() (*wsMessage, error) {
select {
case msg := <-wsConn.inChan:
return msg, nil
case <-wsConn.closeChan:
}
return nil, errors.New("websocket closed")
}
func (wsConn *wsConnection) wsClose() {
wsConn.wsSocket.Close()
wsConn.mutex.Lock()
defer wsConn.mutex.Unlock()
if !wsConn.isClosed {
wsConn.isClosed = true
close(wsConn.closeChan)
}
}
func main() {
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("0.0.0.0:7777", nil)
}前端代碼 client.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
const output = document.getElementById("output");
const input = document.getElementById("input");
let ws;
const print = function(message) {
const d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("ws://localhost:7777/ws");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
</p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>到此這篇關(guān)于Go語言中websocket的使用demo分享的文章就介紹到這了,更多相關(guān)Go語言 websocket內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang 實現(xiàn) Redis系列(六)如何實現(xiàn) pipeline 模式的 redis 客戶端
pipeline 模式的 redis 客戶端需要有兩個后臺協(xié)程負(fù)責(zé) tcp 通信,調(diào)用方通過 channel 向后臺協(xié)程發(fā)送指令,并阻塞等待直到收到響應(yīng),本文是使用 golang 實現(xiàn) redis 系列的第六篇, 將介紹如何實現(xiàn)一個 Pipeline 模式的 Redis 客戶端。2021-07-07
golang通過context控制并發(fā)的應(yīng)用場景實現(xiàn)
這篇文章主要介紹了golang通過context控制并發(fā)的應(yīng)用場景實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01

