溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

websocket小案例

發(fā)布時(shí)間:2020-07-18 09:09:45 來源:網(wǎng)絡(luò) 閱讀:431 作者:梁十八 欄目:編程語言

瀏覽器客戶端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>websocket測試程序 </title>
    <script>
        window.addEventListener("load", function (evt) {
            var output = document.getElementById("output");
            var input = document.getElementById("input")
            var ws;
            var print = function (message) {
                var d = document.createElement("div");
                d.innerHTML = message;
                output.appendChild(d);
            };

            document.getElementById("open").onclick = function (ev) {
                if (ws) {
                    return false;
                }
                ws = new WebSocket("ws://127.0.0.1:8888/ws");
                ws.onopen = function (ev) {
                    print("連接成功");
                };
                ws.onclose = function (ev) {
                    print("連接關(guān)閉");
                };
                ws.onerror = function (ev) {
                    print("發(fā)生錯(cuò)誤 " + ev.data)
                };

                ws.onmessage = function (ev1) {
                    print("服務(wù)端消息: " + ev1.data)
                };

                return false
            };

            document.getElementById("send").onclick = function (ev) {
                if (!ws) {

                    return false
                }

                if (input.value !== "") {
                    ws.send(input.value)
                } else {
                    print("發(fā)送內(nèi)容不能為空")
                }

            };

            document.getElementById("close").onclick = function (ev) {
                if (ws) {
                    ws.close()
                }
            }

        })

    </script>
</head>
<body>

<div>

    <br>
    websocket測試程序,消息又客戶端發(fā)送到server然后原封不動(dòng)的返回,server使用go實(shí)現(xiàn)
    <br>
    <br>
    <br>
    <input type="button" value="連接" id="open">
    <input placeholder="輸入要發(fā)送的消息..." id="input">
    <input type="button" value="發(fā)送" id="send">
    <input type="button" value="關(guān)閉" id="close">

</div>
<div id="output">

</div>

</body>
</html>

版本一:

websocket小案例

package main

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

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

func wsHandler(writer http.ResponseWriter, request *http.Request) {
    var (
        conn *websocket.Conn
        err error
        //msgType int
        data []byte
    )
    //完成握手應(yīng)答
    if conn, err = upgrader.Upgrade(writer, request, nil); err != nil {
        return
    }

    //數(shù)據(jù)收發(fā)
    for {
        //數(shù)據(jù)類型有text、binary,此處選text
        if _, data, err = conn.ReadMessage(); err != nil {
            goto ERR
        }
        if err = conn.WriteMessage(websocket.TextMessage, data); err != nil {
            goto ERR
        }
    }
ERR:
    conn.Close()

}

func main() {
    http.HandleFunc("/ws", wsHandler)
    http.ListenAndServe("127.0.0.1:8888", nil)
}

版本一未做優(yōu)化

版本二:

websocket小案例

connection.go:

package impl

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

type Connection struct {
    wsConn *websocket.Conn   // 底層websocket
    inChan chan []byte      // 讀隊(duì)列
    outChan chan []byte     //  寫隊(duì)列
    closeChan chan byte    // 關(guān)閉通知
    isClosed bool
    mutex sync.Mutex       // 避免重復(fù)關(guān)閉管道
}

//封裝websocket長連接
func InitConnection(wsConn *websocket.Conn) (conn *Connection, err error) {
    conn = &Connection{
        wsConn: wsConn,
        inChan: make(chan []byte, 1000),
        outChan: make(chan []byte, 1000),
        closeChan: make(chan byte, 1),
    }

    //啟動(dòng)讀協(xié)程
    go conn.readLoop()

    //啟動(dòng)寫協(xié)程
    go conn.writeLoop()

    return
}

func (conn *Connection) ReadMessage() (data []byte, err error) {
    select {
    case data = <- conn.inChan:
    case <- conn.closeChan:
        err = errors.New("connection is closed")
    }
    return
}

func (conn *Connection) WriteMessage(data []byte) (err error) {
    select {
    case conn.outChan <- data:
    case <- conn.closeChan:
        err = errors.New("connection is closed")
    }
    return
}

func (conn *Connection) Close() {
    // wsConn.Close是線程安全的,可重入的(可以多次關(guān)閉)
    conn.wsConn.Close()
    //一個(gè)chan只能關(guān)閉一次(所以要保證這行代碼只執(zhí)行一次)
    conn.mutex.Lock()
    if !conn.isClosed{
        close(conn.closeChan)
        conn.isClosed = true
    }
    conn.mutex.Unlock()
}

//內(nèi)部實(shí)現(xiàn)
func (conn *Connection) readLoop() {
    var (
        data []byte
        err error
    )
    //不停的讀
    for {
        if _, data, err = conn.wsConn.ReadMessage(); err != nil {
            goto ERR
        }
        //阻塞在這里,等待inChan有空閑位置
        select {
        case conn.inChan <- data:
        case <- conn.closeChan: //當(dāng)closeChan被關(guān)閉就進(jìn)入這個(gè)分支
            goto ERR
        }

    }
ERR:
    conn.Close()
}

func (conn *Connection) writeLoop() {
    var (
        data []byte
        err error
    )
    //不停寫
    for {
        select {
        case data = <-conn.outChan:
        case <- conn.closeChan:
            goto ERR
        }
        if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil{
            goto ERR
        }
    }
ERR:
    conn.Close()
}

server.go:

package main

import (
    "net/http"
    "github.com/gorilla/websocket"
    "./impl"
    "time"
)

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

func wsHandler(writer http.ResponseWriter, request *http.Request) {
    var (
        wsConn *websocket.Conn
        err error
        //msgType int
        data []byte
        conn *impl.Connection
    )
    //完成握手應(yīng)答
    if wsConn, err = upgrader.Upgrade(writer, request, nil); err != nil {
        return
    }

    if conn, err = impl.InitConnection(wsConn); err != nil {
        goto ERR
    }

    // 不停發(fā)送心跳信息
    go func() {
        var (
            err error
        )
        for {
            if err = conn.WriteMessage([]byte("heartbeat")); err != nil {
                return
            }
            time.Sleep(time.Second)
        }
    }()

    for {
        if data, err = conn.ReadMessage(); err != nil {
            goto ERR
        }
        if err = conn.WriteMessage(data); err != nil {
            goto ERR
        }
    }

ERR:
    // 關(guān)閉連接的操作
    conn.Close()

    /*//數(shù)據(jù)收發(fā)
    for {
        //數(shù)據(jù)類型有text、binary,此處選text
        if _, data, err = conn.ReadMessage(); err != nil {
            goto ERR
        }
        if err = conn.WriteMessage(websocket.TextMessage, data); err != nil {
            goto ERR
        }
    }
ERR:
    conn.Close()*/

}

func main() {
    http.HandleFunc("/ws", wsHandler)
    http.ListenAndServe("127.0.0.1:8888", nil)
}

做了封裝,線程安全

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI