Go語言【項目】 websocket消息服務

来源:https://www.cnblogs.com/lianzhilei/archive/2019/09/06/11478190.html
-Advertisement-
Play Games

websocket消息服務 目的:搭建websocket服務,用瀏覽器與服務進行消息交互(寫的第一個Go程式) 代碼目錄結構: 前端html頁面: 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <script> 6 wi ...


websocket消息服務

 

目的:搭建websocket服務,用瀏覽器與服務進行消息交互(寫的第一個Go程式)

 

代碼目錄結構:

 

前端html頁面:

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8">
 5     <script>
 6         window.addEventListener("load", function(evt) {
 7             var output = document.getElementById("output");
 8             var input = document.getElementById("input");
 9             var ws;
10             var print = function(message) {
11                 var d = document.createElement("div");
12                 d.innerHTML = message;
13                 output.appendChild(d);
14             };
15             document.getElementById("open").onclick = function(evt) {
16                 if (ws) {
17                     return false;
18                 }
19                 ws = new WebSocket("ws://127.0.0.1:7777/ws");
20                 ws.onopen = function(evt) {
21                     print("OPEN");
22                 }
23                 ws.onclose = function(evt) {
24                     print("CLOSE");
25                     ws = null;
26                 }
27                 ws.onmessage = function(evt) {
28                     print("RESPONSE: " + evt.data);
29                 }
30                 ws.onerror = function(evt) {
31                     print("ERROR: " + evt.data);
32                 }
33                 return false;
34             };
35             document.getElementById("send").onclick = function(evt) {
36                 if (!ws) {
37                     return false;
38                 }
39                 print("SEND: " + input.value);
40                 ws.send(input.value);
41                 return false;
42             };
43             document.getElementById("close").onclick = function(evt) {
44                 if (!ws) {
45                     return false;
46                 }
47                 ws.close();
48                 return false;
49             };
50         });
51     </script>
52 </head>
53 <body>
54 <table>
55     <tr><td valign="top" width="50%">
56         <p>Click "Open" to create a connection to the server,
57             "Send" to send a message to the server and "Close" to close the connection.
58             You can change the message and send multiple times.
59         </p>
60             <form>
61                 <button id="open">Open</button>
62                 <button id="close">Close</button>
63             <input id="input" type="text" value="Hello world!">
64             <button id="send">Send</button>
65             </form>
66     </td><td valign="top" width="50%">
67         <div id="output"></div>
68     </td></tr></table>
69 </body>
70 </html>
client.html

 

server.go代碼:

package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"go_websocket"
	"net/http"
)

// http升級websocket協議的配置
var wsUpgrader = websocket.Upgrader{
	// 允許跨域CORS
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// 消息處理
func wsHandler(resp http.ResponseWriter, req *http.Request) {
	wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
	if err != nil {
		return
	}
	wsConn := go_websocket.WsConnectionInit(wsSocket)
	wsConn.Run()

	for {
		wsmsg, err := wsConn.ReadMessage()
		if err != nil {
			goto error
		}
		err = wsConn.WriteMessage(wsmsg)
		if err != nil {
			goto error
		}
	}
error:
	fmt.Println("websocket is closed")
	return
}

func main() {
	fmt.Println("websocket start")
	http.HandleFunc("/ws", wsHandler)
	http.ListenAndServe("0.0.0.0:7777", nil)
}

 

connection.go代碼:

package go_websocket

import (
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"sync"
	"time"
)

// 客戶端讀寫消息
type WsMessage struct {
	msgType int
	data    []byte
}

// 客戶端連接
type wsConnection struct {
	wsSocket *websocket.Conn
	inChan   chan *WsMessage
	outChan  chan *WsMessage

	isClosed  bool
	closeChan chan []byte
	mutex     sync.Mutex
}

// 連接初始化
func WsConnectionInit(wsSocket *websocket.Conn) (wsConn *wsConnection) {
	wsConn = &wsConnection{
		wsSocket:  wsSocket,
		inChan:    make(chan *WsMessage, 1000),
		outChan:   make(chan *WsMessage, 1000),
		closeChan: make(chan []byte, 1),
	}
	return wsConn
}

// 啟動
func (wsConn *wsConnection) Run() {
	go wsConn.readLoop()
	go wsConn.writeLoop()
	go wsConn.heartbeat()
}

// 心跳檢測
func (wsConn *wsConnection) heartbeat() {
	for {
		time.Sleep(2 * time.Second)
		wsmsg := &WsMessage{msgType: websocket.TextMessage, data: []byte("heartbeat")}
		err := wsConn.WriteMessage(wsmsg)
		if err != nil {
			fmt.Println("send heartbeat stop")
			return
		}
	}
}

// 迴圈接收
func (wsConn *wsConnection) readLoop() {
	var ()

	for {
		msgType, data, err := wsConn.wsSocket.ReadMessage()
		if err != nil {
			goto error
		}
		select {
		case wsConn.inChan <- &WsMessage{msgType: msgType, data: data}:
		case <-wsConn.closeChan:
			goto closed
		}
	}
error:
	wsConn.Close()
closed:
	fmt.Println("readLoop closed")
}

// 迴圈發送
func (wsConn *wsConnection) writeLoop() {
	for {
		select {
		case wsmsg := <-wsConn.outChan:
			if err := wsConn.wsSocket.WriteMessage(wsmsg.msgType, wsmsg.data); err != nil {
				goto error
			}
		case <-wsConn.closeChan:
			goto closed
		}
	}
error:
	wsConn.Close()
closed:
	fmt.Println("writeLoop close")
}

// 取消息,外部可調用
func (wsConn *wsConnection) ReadMessage() (wsmsg *WsMessage, err error) {
	select {
	case wsmsg = <-wsConn.inChan:
		return wsmsg, nil
	case <-wsConn.closeChan:
		return nil, errors.New("websocket is closed")
	}
}

// 寫消息,外部可調用
func (wsConn *wsConnection) WriteMessage(wsmsg *WsMessage) (err error) {
	select {
	case wsConn.outChan <- wsmsg:
	case <-wsConn.closeChan:
		return errors.New("websocket is closed")
	}
	return nil
}

// 關閉wsSocket
func (wsConn *wsConnection) Close() {
	wsConn.wsSocket.Close()

	// 加鎖
	wsConn.mutex.Lock()
	if !wsConn.isClosed {
		wsConn.isClosed = true
		close(wsConn.closeChan)
	}
	wsConn.mutex.Unlock()
}

  

效果展示:

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 閱讀本文大概需要 4 分鐘。 作者:黃小斜 不知道大家知不知道彈性工作制這種說法。 這個說法據說最早也是出現在互聯網公司里的,所謂的彈性工作制是什麼意思呢?簡單來說,就是指,員工的工作時間可以隨著工作的情況來進行適當的調整。 工作輕鬆的時候,每天工作時間縮短幾小時,工作比較忙的時候,工作時間就延長幾 ...
  • 02-jQuery的選擇器 我們以前在CSS中學習的選擇器有: 今天來學習一下jQuery 選擇器。 jQuery選擇器是jQuery強大的體現,它提供了一組方法,讓我們更加方便的獲取到頁面中的元素。 1、jQuery 的基本選擇器 代碼如下: <!DOCTYPE html> <html lang= ...
  • 今日所學: /* 2019.08.19開始學習,此為補檔。 */ JDK 1.6:byte , int , short , char , enum JDK 1.7:byte , int , short , char , enum , String 數組:相同類型的容器。其 a.大小固定 b.記憶體連續 ...
  • 上面的結果返回為null getResource方法要註意類路徑 測試後發現方法參數為 ,返回的url是類路徑的。 maven工程編譯後的class文件目錄結構的類路徑,不需要以 開頭。 就正常了 ``` ...
  • java模擬多ip請求 package url_demo; import java.util.Random; public class DemoUtl { public static int index = 0; public static void main(String[] args) thro ...
  • 場景 CSDN: https://blog.csdn.net/badao_liumang_qizhi 博客園: https://www.cnblogs.com/badaoliumangqizhi/ 嗶哩嗶哩視頻教程: https://space.bilibili.com/164396311 實現 關 ...
  • 一、此處主要介紹在springboot工程下如何使用 logback + slf4j 進行日誌記錄。 logback主要包含三個組成部分:Loggers(日誌記錄器)、Appenders(輸出目的在)、Layouts(日誌輸出格式) slf4j :如jdbc一樣,定義了一套介面,是一個日誌門面,可實 ...
  • 引言 我們知道開發最好用Mac/Linux,效率很高,但是對於很多還是Windows用戶的我們來說,編寫代碼再到linux上運行也是很常有的事情,但對於我們寫一些小demo使用上面的流程難免有點興師動眾,傷元氣的事情程式員只會掉發更快,所以再Windows搭建gcc開發環境還是很有必要的,MinGW ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...