美文网首页Go语言的不归路
Go构建WebSocket服务器和客户端

Go构建WebSocket服务器和客户端

作者: 吴佳浩 | 来源:发表于2023-11-01 15:31 被阅读0次

    简介

    本教程将教你如何使用Go语言构建WebSocket服务器和客户端,以实现双向
    通信。我们将介绍如何创建一个WebSocket服务器,用于接收来自客户端的消息,以及如何创建一个WebSocket客户端,用于与服务器通信。我们还将介绍一个简单的HTML页面,演示如何在浏览器中使用WebSocket客户端与服务器进行通信。

    操作

    1.创建一个文件夹.

    2.使用GO mod初始化go mod init your_module_name

    3.创建文件

    4.go get github.com/gorilla/websocket

    WebSocket 服务器

    首先,我们将创建一个WebSocket服务器,用于接收来自客户端的消息。我们使用github.com/gorilla/websocket包来处理WebSocket连接。

    WsServer.go

    // 导入必要的包
    import (
        "fmt"
        "net"
        "net/http"
        "time"
        "github.com/gorilla/websocket"
    )
    
    // 创建WsServer结构体
    type WsServer struct {
        listener net.Listener
        addr     string
        upgrade  *websocket.Upgrader
    }
    
    // 初始化WsServer
    func NewWsServer() *WsServer {
        ws := new(WsServer)
        ws.addr = "0.0.0.0:10215"
        ws.upgrade = &websocket.Upgrader{
            ReadBufferSize:  4096,
            WriteBufferSize: 1024,
            CheckOrigin: func(r *http.Request) bool {
                if r.Method != "GET" {
                    fmt.Println("method is not GET")
                    return false
                }
                if r.URL.Path != "/ws" {
                    fmt.Println("path error")
                    return false
                }
                return true
            },
        }
        return ws
    }
    
    // 处理WebSocket连接
    func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/ws" {
            httpCode := http.StatusInternalServerError
            reasePhrase := http.StatusText(httpCode)
            fmt.Println("path error ", reasePhrase)
            http.Error(w, reasePhrase, httpCode)
            return
        }
        conn, err := self.upgrade.Upgrade(w, r, nil)
        if err != nil {
            fmt.Println("websocket error:", err)
            return
        }
        fmt.Println("client connect:", conn.RemoteAddr())
        go self.connHandle(conn)
    }
    
    // 处理WebSocket连接中的消息
    func (self *WsServer) connHandle(conn *websocket.Conn) {
        defer func() {
            conn.Close()
        }()
        stopCh := make(chan int)
        go self.send(conn, stopCh)
        for {
            conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000)))
            _, msg, err := conn.ReadMessage()
            if err != nil {
                close(stopCh)
                if netErr, ok := err.(net.Error); ok {
                    if netErr.Timeout() {
                        fmt.Printf("ReadMessage timeout remote: %v\n", conn.RemoteAddr())
                        return
                    }
                }
                if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
                    fmt.Printf("ReadMessage other remote:%v error: %v \n", conn.RemoteAddr(), err)
                }
                return
            }
            fmt.Println("Received message:", string(msg))
        }
    }
    
    // 向客户端发送消息
    func (self *WsServer) send(conn *websocket.Conn, stopCh chan int) {
        self.send10(conn)
        for {
            select {
            case <-stopCh:
                fmt.Println("connection closed")
                return
            case <-time.After(time.Second * 1):
                data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano())
                err := conn.WriteMessage(1, []byte(data))
                fmt.Println("Sending....")
                if err != nil {
                    fmt.Println("send message failed ", err)
                    return
                }
            }
        }
    }
    
    // 测试一次性发送10万条数据给客户端
    func (self *WsServer) send10(conn *websocket.Conn) {
        for i := 0; i < 100000; i++ {
            data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano())
            err := conn.WriteMessage(1, []byte(data))
            if err != nil {
                fmt.Println("send message failed ", err)
                return
            }
        }
    }
    
    // 启动WebSocket服务器
    func (w *WsServer) Start() (err error) {
        w.listener, err = net.Listen("tcp", w.addr)
        if err != nil {
            fmt.Println("net listen error:", err)
            return
        }
        err = http.Serve(w.listener, w)
        if err != nil {
            fmt.Println("http serve error:", err)
            return
        }
        return nil
    }
    
    func main() {
        ws := NewWsServer()
        ws.Start()
    }
    

    这是WebSocket服务器的代码。它创建一个WsServer结构体,用于处理WebSocket连接。服务器会不断接收来自客户端的消息,并发送一些测试消息。你可以根据需要进行修改和扩展。

    WebSocket 客户端

    接下来,我们将创建一个WebSocket客户端,用于与服务器进行通信。我们将使用自定义的WebSocket客户端实现。

    WsClient.go

    package main
    
    import (
        "fmt"
        "net"
        "net/http"
        "time"
        "github.com/gorilla/websocket"
    )
    
    // 创建自定义WebSocket客户端
    type VIL struct{}
    
    type DefaultWebSocket struct {
        _host    string
        _isOpen  bool
        _bufQueue []interface{}
        _bufCap  int
        _call    interface{}
        _socket  *websocket.Conn
    }
    
    // 初始化自定义WebSocket客户端
    func (self *VIL) DefaultWebSocket(host, call) {
        _host = host
        _isOpen = false
        _bufQueue = []interface{}
        _bufCap = 100
        if call != nil {
            _call = call
        } else {
            _call = {
                onConnect: func(e) {
                    fmt.Println("connect success ", e)
                },
                onDisconnect: func(e) {
                    fmt.Println("disconnect ", e)
                },
                onMsg: func(data) {
                    // fmt.Println("receive message ", data)
                },
            }
        }
    
        _socket = new(websocket.Conn)
        _socket = websocket.DefaultDialer.Dial(_host, nil)
        _socket.binaryType = "arraybuffer"
    }
    
    // 设置发送消息缓存队列的容量
    func (self *DefaultWebSocket) setBufferCap(cap) {
        if cap < 0 {
            fmt.Println("parameter value can not be less than 0")
            return
        }
        _bufCap = cap
    }
    
    // 发送消息
    func (self *DefaultWebSocket) send(data) {
    
    
        if _isOpen && _socket != nil {
            _socket.send("")
        } else {
            if len(_bufQueue) < _bufCap {
                _bufQueue = append(_bufQueue, data)
            }
        }
    }
    
    // 关闭WebSocket连接
    func (self *DefaultWebSocket) close() {
        _socket.Close(1000, "normal")
        _isOpen = false
    }
    
    // 处理WebSocket连接中的消息
    func (self *DefaultWebSocket) handle() {
        go func() {
            for {
                _socket.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000)))
                _, message, err := _socket.ReadMessage()
                if err != nil {
                    fmt.Println("Error while reading message:", err)
                    return
                }
                _call.onMsg(message)
            }
        }()
    }
    
    // 向客户端发送消息
    func (self *DefaultWebSocket) sendToServer() {
        go func() {
            for {
                select {
                case <-stopCh:
                    fmt.Println("Interrupted, closing connection...")
                    err := _socket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
                    if err != nil {
                        fmt.Println("Error while sending close message:", err)
                        return
                    }
                    select {
                    case <-done:
                    case <-time.After(time.Second):
                    }
                    return
                case <-time.After(time.Second * 1):
                    data := fmt.Sprintf("Hello WebSocket test from client %v", time.Now().UnixNano())
                    err := _socket.WriteMessage(1, []byte(data))
                    if err != nil {
                        fmt.Println("Error while sending message:", err)
                        return
                    }
                }
            }
        }()
    }
    

    这是WebSocket客户端的代码。它创建了一个DefaultWebSocket结构体,用于处理WebSocket连接。客户端会不断发送消息给服务器,并处理来自服务器的消息。你可以根据需要进行修改和扩展。

    HTML 页面

    最后,我们创建一个简单的HTML页面,用于在浏览器中使用WebSocket客户端与服务器进行通信。以下是HTML代码:

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>WebSocket Example</title>
    </head>
    <body>
    </body>
    <script type="text/javascript" src="./index.js"></script>
    <script>
        let counter = 0;
        let isConnect = false;
        let handler = {
            onConnect: function (e) {
                isConnect = true;
                console.log("handler connect success ", e);
                var se = setInterval(function () {
                    if (isConnect === false) {
                        clearInterval(se);
                    }
                    console.log("setInterval", Date.now());
                    socket.send("Web browser setInterval");
                }, 3000);
            },
            onDisconnect: function (e) {
                isConnect = false;
                console.log("handler disconnect ", e);
            },
            onMsg: function (data) {
                counter++;
                if (counter >= 2000) {
                    counter = 0;
                    console.log("handler receive message ", data);
                }
            }
        };
    
        let socket = new VIL.DefaultWebSocket("ws://127.0.0.1:10215/ws", handler);
    </script>
    </html>
    

    index.js

    let VIL = (function () {
        let VIL = {
        };
    
        function DefaultWebSocket(host, call) {
            let _host = host;
            let _isOpen = false;
            let _bufQueue = [];
            let _bufCap = 100;
            let _call = null;
            if("undefined" !== typeof call && call !== null){
                _call = call
            }else{
                _call = {
                    onConnect:function (e) {
                        console.log("connect success ", e);
                    },
                    onDisconnect:function (e) {
                        console.log("disconnect ", e);
                    },
                    onMsg:function (data) {
                        //console.log("receive message ", data)
                    }
                }
            }
    
    
            let _socket = new WebSocket(_host);
            _socket.binaryType = "arraybuffer";
            /**
             * 设置发送消息缓存队列的容量
             * @param {number} cap
             * @constructor
             */
            this.setBufferCap = function(cap){
                if("number" !== typeof cap ){
                    console.error("parameter type is not number ");
                    return ;
                }
                if(cap < 0){
                    console.error("parameter value can not less then 0");
                    return ;
                }
                _bufCap = cap;
            };
    
            /**
             * 发送消息
             * @param {string | ArrayBuffer } data
             * @constructor
             */
            this.send = function(data){
                if(_isOpen && _socket){
                    _socket.send("");
                }else{
                    if (_bufQueue < _bufCap){
                        _bufQueue.push(data);
                    }
                }
            };
    
            this.close = function(){
                _socket.close(1000, "normal");
            };
    
            _socket.onopen = function(even){
                _isOpen = true;
                _call.onConnect(even);
                while (_bufQueue > 0){
                    _socket.send(_bufQueue.shift());
                }
            };
    
            _socket.onmessage = function(e){
                let data = e.data;
                _call.onMsg(data);
            };
    
            /**
             * 收到关闭连接
             * @param even
             */
            _socket.onclose = function(even){
                _isOpen = false;
                _call.onDisconnect({host:_host, event:even});
            };
    
            /**
             * 收到错误
             * @param err
             */
            _socket.onerror = function(err){
                _isOpen = false;
                _call.onDisconnect({host:_host, event:err});
            };
        }
    
        try{
            VIL.EngineSocket = DefaultWebSocket ;
        }catch (e) {
            console.error("VILEngine error ", e);
        }
    
        return VIL;
    })();
    
    

    这个HTML页面包括一个WebSocket客户端的JavaScript代码,它会连接到我们的服务器,并处理连接、断开连接和消息。在浏览器中打开这个页面后,你将能够与服务器进行WebSocket通信。

    总结

    在本教程中,我们创建了一个WebSocket服务器和客户端,并演示了如何在浏览器中使用WebSocket客户端与服务器进行通信。WebSocket提供了一种强大的方式来实现实时双向通信,适用于各种应用程序,如在线聊天、实时数据更新等。你可以根据需要进一步扩展这些示例,以构建更复杂的WebSocket应用程序。希望这个教程对你有所帮助!

    相关文章

      网友评论

        本文标题:Go构建WebSocket服务器和客户端

        本文链接:https://www.haomeiwen.com/subject/fkcwqltx.html