1. 程式人生 > >GO 語言websocket程式設計

GO 語言websocket程式設計

GO提供原生的websocket API,使用時go get然後引用即可golang.org/x/net/websocket 使用起來也很方便,直接上程式碼吧。

一個echo server的程式碼

package main

import(
    "golang.org/x/net/websocket"
    "fmt"
    "net/http"
    "flag"
)

type WSServer struct {
    ListenAddr string
}

func (this *WSServer)handler(conn *websocket.Conn){
    fmt.Printf("a new ws conn: %s->%s\n", conn.RemoteAddr().String(), conn.LocalAddr().String())
    var err error
    for {
        var reply string
        err = websocket.Message.Receive(conn, &reply)
        if err != nil {
            fmt.Println("receive err:",err.Error())
            break
        }
        fmt.Println("Received from client: " + reply)
        if err = websocket.Message.Send(conn, reply); err != nil {
            fmt.Println("send err:", err.Error())
            break
        }
    }
}
func (this *WSServer)start()(error){
    http.Handle("/ws", websocket.Handler(this.handler))
    fmt.Println("begin to listen")
    err := http.ListenAndServe(this.ListenAddr, nil)
    if err != nil {
        fmt.Println("ListenAndServe:", err)
        return err
    }
    fmt.Println("start end")
    return nil
}

func main(){
    addr  := flag.String("a", "127.0.1.1:12345", "websocket server listen address")
    flag.Parse()
    wsServer := &WSServer{
        ListenAddr : *addr,
    }
    wsServer.start()
    fmt.Println("------end-------")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

上述程式碼中,每來一個新的websocket client,server會起一個goroutine執行WSSever的handler函式。

websocket client程式碼例項

package main

import (
    "flag"
    "fmt"
    "time"
    "golang.org/x/net/websocket"
)

var addr = flag.String("addr", "127.0.0.1:12345", "http service address")

func main() {
    flag.Parse()

    url := "ws://"+ *addr + "/ws"
    origin := "test://1111111/"
    ws, err := websocket.Dial(url, "", origin)
    if err != nil {
        fmt.Println(err)
    }
    go timeWriter(ws)

    for {
        var msg [512]byte
        _, err := ws.Read(msg[:])//此處阻塞,等待有資料可讀
        if err != nil {
            fmt.Println("read:", err)
            return
        }

        fmt.Printf("received: %s\n", msg)
    }
}

func timeWriter(conn *websocket.Conn) {
    for {
        time.Sleep(time.Second * 2)
        websocket.Message.Send(conn, "hello world")
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

client的程式碼,每隔2秒鐘傳送hello world到server,然後阻塞在Read函式。需要注意的是origin必須以“http://1111111/” 這種標準的URI格式,否則報錯“invalid URI for request”。

關閉程序、網路斷線等異常情況

關閉程序

無論server還是client,關閉程序,對端Read都會立刻收到EOF,對EOF做處理即可。

網路斷線

  • 測試 client和server部署在不同機器上,client每隔2秒中向server傳送資料,server收到後回吐給客戶端。這個過程中,拔掉client的網線。
  • 測試結果
    1. 斷網後,client一定時間內寫都能成功返回,但是因為斷網實際沒有傳送出去,資料寫到了底層tcp的緩衝區。
    2. 過一段時間後,1分鐘左右,client Read返回錯誤“read: operation timed out”。Write會返回“write: broken pipe”。這個可能是Go中websocket實現時加了超時機制,也有可能是設定了底層TCP SO_KEEPALIVE,檢測到了網路不可用。
    3. 在Read/Write返回錯誤之前,重新連上網路,可以繼續傳送和接受資料。這個可以從TCP的實現上解釋。TCP連線並不是物理連線,本質上就是連線兩端各自系統核心維護的一個四元組。客戶端斷線,在一定時間內並不會導致四元組的釋放。所以當連上網線後此TCP連線可以自動恢復,繼續進行正常的網路操作。
    4. 斷線重連到其他網路,相當於斷網。這個很好解釋,連上其他網路,IP地址都改變了,之前的四元組不可用。

--------------------- 本文來自 阿冬哥 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/c359719435/article/details/78845719?utm_source=copy