Go語言實現的WebSocket

作者:豌豆射手_BiuBiu 鏈接:https://www.jianshu.com/p/a5b187f7e669 來源:簡書

  • 最終的效果如下

Web端上傳的信息

Web端得到的打印的信息

服務端的代碼的實現

服務端的信息

  • WebSocket協議是基於TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工(full-duplex)通信——允許服務器主動發送信息給客戶端。WebSocket通信協議於2011年被IETF定爲標準RFC 6455,並被RFC7936所補充規範。WebSocket是HTML5的重要特性,它實現了基於瀏覽器的遠程socket,它使瀏覽器和服務器可以進行全雙工通信,許多瀏覽器Firefox、Google Chrome和Safari都已對此做了支持
  • WebSocket出現之前,爲了實現即時通信,採用的技術都是“輪詢”,即在特定的時間間隔內,由瀏覽器對服務器發出HTTP Request,服務器在收到請求後,返回最新的數據給瀏覽器刷新,“輪詢”使得瀏覽器需要對服務器不斷髮出請求,這樣會佔用大量帶寬。
  • 很多的遊戲的服務都是採用的是Socket,因爲HTTP協議相對而言比較耗費性能, 隨着HTML5的發展,WebSocket也逐漸發展成爲很多頁遊公司接下來開發的一些手段。
  • 安卓推送的原理: C2DM 推送 (Google) C2DM 推送簡介 : 全稱 Cloudto Device Messaging, Google 提供的 推送解決方案;
    • 運行方式 : 提供一個輕量級機制, 允許服務器通知應用程序, 主動與客戶端進行數據交互, 處理消息排隊, 並向運行於目標設備的應用程序分發消息;
    • 優點 : Google 提供的原生框架, 無需在應用中添加第三方代碼 和 部署服務器端;
    • 缺點 : 1.該推送依賴 Google 服務器, 需要綁定 Google 帳號, 目前在中國 Google 被屏蔽, 無法使用; 2. 許多手機廠商去掉了軟件中的該模塊;
  • 極光推送的原理:因爲IP v4 的 IP 量有限,運營商分配給手機終端的 IP 是運營商內網的 IP,手機要連接 Internet,就需要通過運營商的網關做一個網絡地址轉換Network Address Translation,NAT。簡單的說運營商的網關需要維護一個外網 IP、端口到內網 IP、端口的對應關係,以確保內網的手機可以跟 Internet 的服務器通訊。
  • Android 平臺上長連接的實現
    • Timer Android 的 Timer 類可以用來計劃需要循環執行的任務,Timer 的問題是它需要用 WakeLock 讓 CPU 保持喚醒狀態,這樣會大量消耗手機電量,大大減短手機待機時間。
    • AlarmManager 這篇文章有介紹怎麼使用AlarmManager安卓網絡和電量優化 AlarmManager 是 Android 系統封裝的用於管理 RTC 的模塊,RTC (Real Time Clock) 是一個獨立的硬件時鐘,可以在 CPU 休眠時正常運行,在預設的時間到達時,通過中斷喚醒 CPU。 這意味着,如果我們用 AlarmManager 來定時執行任務,CPU 可以正常的休眠,只有在需要運行任務時醒來一段很短的時間。極光推送的 Android SDK 就是基於這種技術實現的。極光官方文檔
  • WebSocket URL的起始輸入是ws://或是wss://(在SSL上)。一個帶有特定報頭的HTTP握手被髮送到了服務器端,接着在服務器端或是客戶端就可以通過JavaScript來使用某種套接口(socket),這一套接口可被用來通過事件句柄異步地接收數據。

WebSocket 原理

  • WebSocket的協議:在第一次handshake通過以後,連接便建立成功,其後的通訊數據都是以”\x00″開頭,以”\xFF”結尾。在客戶端,這個是透明的,WebSocket組件會自動將原始數據“掐頭去尾”。

request的信息.png

 1GET http://localhost:8080/shiming HTTP/1.1
 2Host: localhost:8080
 3Connection: Upgrade
 4Pragma: no-cache
 5Cache-Control: no-cache
 6Upgrade: websocket
 7Origin: file://
 8Sec-WebSocket-Version: 13
 9User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0
10Accept-Encoding: gzip, deflate, sdch, br
11Accept-Language: zh-CN,zh;q=0.8
12Cookie: _ga=GA1.1.27955907.1529919744
13Sec-WebSocket-Key: PCD+pA79juC6tlBK9zD3Vw==
14Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
  • Sec-WebSocket-Key這個是個隨機的值,是一個經過base64編寫後的數據Sec-WebSocket-Key: PCD+pA79juC6tlBK9zD3Vw==
  • 然後服務器收到這個請求之後把這個字符串連接上一個固定的字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 爲啥是這樣的,我目前還不明白,僅僅是自己記錄下而已。
  • 最終得到:
1PCD+pA79juC6tlBK9zD3Vw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
  • 對該字符使用shal 安全散列算法計算出二進制的值,然後用base64 對其進行編碼,即可以得到握手的字符串: 8+5CLWTBYLARKoxBxS5uk6s6zZo= ,如下圖所示

response信息.png

1HTTP/1.1 101 Switching Protocols
2Upgrade: websocket
3Connection: Upgrade
4Sec-WebSocket-Accept: 8+5CLWTBYLARKoxBxS5uk6s6zZo=
  • Sec-WebSocket-Accept作爲響應頭的值反饋給客戶端。

Go語言實現Websocket

  • 由於Go語言標準包裏面沒有對WebSocket的支持,但是官方維護的go.net對這個有支持,所以可以獲取
1go get golang.org/net/websocket
  • 但是有個小問題,當我 go get後,我在代碼中導入包會報錯,同時去掉x也不行,所以我在本地目錄創建了一個x的目錄,然後把net全部放進去了

注意問題.png

導包

  • html 代碼
 1<html>
 2<head>
 3    <title>好好學習</title>
 4</head>
 5<body>
 6<script type="text/javascript">
 7    var sock = null;
 8    // var wsuri = "wss://127.0.0.1:8080"; //本地的地址 是可以改變的哦
 9     var wsuri = "ws://localhost:8080/shiming"; //本地的地址 是可以改變的哦
10
11
12    window.onload = function() {
13        //可以看到客戶端JS,很容易的就通過WebSocket函數建立了一個與服務器的連接sock,當握手成功後,會觸發WebScoket對象的onopen事件,告訴客戶端連接已經成功建立。客戶端一共綁定了四個事件。
14        console.log("開始了 onload");
15
16        sock = new WebSocket(wsuri);
17        //建立連接後觸發
18        sock.onopen = function() {
19            console.log(" 建立連接後觸發 connected to " + wsuri);
20        }
21        // 關閉連接時候觸發
22        sock.onclose = function(e) {
23            console.log("關閉連接時候觸發 connection closed (" + e.code + ")");
24        }
25        // 收到消息後觸發
26        sock.onmessage = function(e) {
27            console.log("收到消息後觸發 message received: " + e.data);
28        }
29        //發生錯誤的時候觸發
30        sock.onerror=function (e) {
31            console.log("發生錯誤時候觸發"+wsuri)
32        }
33    };
34     //如果sock被關閉掉了 這裏 也會報錯的啊
35    function send() {
36        var msg = document.getElementById('message').value;
37        sock.send(msg);
38    };
39</script>
40<h1>GoWebSocketDemo</h1>
41<form>
42    <p>
43        Message: <input id="message" type="text" value="你好啊  shiming 小哥哥  嘿嘿   ">
44    </p>
45</form>
46<button onclick="send();">給服務器發送消息</button>
47</body>
48</html>
  • go 代碼
 1package main
 2
 3import (
 4    "fmt"
 5    "net/http"
 6    //草擬嗎 自己創建的目錄 哈哈哈哈哈    還好我比較聰明  要不然 就完蛋了  麻痹
 7    "golang.org/x/net/websocket"
 8    "log"
 9)
10
11func main() {
12    fmt.Println("Go語言標準包裏面沒有提供對WebSocket的支持,但是在由官方維護的go.net子包中有對這個的支持 go get golang.org/x/net/websocket")
13    //打印這個信息就,os.Exit(1)  退出程序
14    //log.Fatal("shiming")  todo  草擬嗎 啊   看清楚啊   後面的域名的地址 有個老子的名字啊
15    http.Handle("/shiming",websocket.Handler(Echo))
16     if err:=http.ListenAndServe(":8080",nil);err!=nil{
17        log.Fatal(err)
18     }
19
20
21}
22
23func Echo(w *websocket.Conn)  {
24    var error error
25    for   {
26        var reply string
27        if  error= websocket.Message.Receive(w,&reply);error!=nil{
28            fmt.Println("不能夠接受消息 error==",error)
29            break
30        }
31        fmt.Println("能夠接受到消息了--- ",reply)
32        msg:="我已經收到消息 Received:"+reply
33        //  連接的話 只能是   string;類型的啊
34        fmt.Println("發給客戶端的消息: "+msg)
35
36        if error = websocket.Message.Send(w, msg); error != nil {
37            fmt.Println("不能夠發送消息 悲催哦")
38            break
39        }
40    }
41}
  • 說明一點:http.Handle("/shiming",websocket.Handler(funName)),如果在這裏有路由的話,記得在 html中也要改成一樣的, html中的代碼 :var wsuri = "ws://localhost:8080/shiming"
  • x目錄自己創建一個,把net包剪切進去就可以

版權申明:內容來源網絡,版權歸原創者所有。除非無法確認,我們都會標明作者及出處,如有侵權煩請告知,我們會立即刪除並表示歉意。謝謝。

Golang語言社區

ID:Golangweb

www.bytedancing.com

遊戲服務器架構丨分佈式技術丨大數據丨遊戲算法學習

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章