Golang构建WebSocket服务器和客户端的示例详解

 更新时间:2023年11月03日 11:25:59   作者:吴佳浩  
这篇文章主要为大家详细介绍了如何使用Go语言构建WebSocket服务器和客户端,以实现双向通信,文中的示例代码讲解详细,需要的小伙伴可以参考一下

简介

本教程将教你如何使用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应用程序。

以上就是Golang构建WebSocket服务器和客户端的示例详解的详细内容,更多关于go WebSocket的资料请关注脚本之家其它相关文章!

相关文章

  • Go 语言中程序编译过程详解

    Go 语言中程序编译过程详解

    本文旨在深入探讨Go语言的编译机制和最新的模块管理系统——Go Modules,通过详细的示例和步骤,我们将演示从简单的 “Hello World” 程序到使用第三方库的更复杂项目的开发过程,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • Golang实现for循环运行超时后自动退出的方法

    Golang实现for循环运行超时后自动退出的方法

    for循环对大家来说应该都不陌生,对于golang来说更是必不可少,所以下面这篇文章就来给大家介绍了关于Golang如何实现for循环运行一段时间超时后自动退出的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-11-11
  • go语言实现顺序存储的栈

    go语言实现顺序存储的栈

    这篇文章主要介绍了go语言实现顺序存储的栈,实例分析了Go语言实现顺序存储的栈的原理与各种常见的操作技巧,需要的朋友可以参考下
    2015-03-03
  • GoLang中Module的基本使用方法

    GoLang中Module的基本使用方法

    Go module是从Go 1.11版本才引入的新功能,下面这篇文章主要给大家介绍了关于GoLang中Module的基本使用方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Golang捕获panic堆栈信息的讲解

    Golang捕获panic堆栈信息的讲解

    今天小编就为大家分享一篇关于Golang捕获panic堆栈信息的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • Go 如何基于IP限制HTTP访问频率的方法实现

    Go 如何基于IP限制HTTP访问频率的方法实现

    这篇文章主要介绍了Go 如何基于IP限制HTTP访问频率的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • 解决Goland中利用HTTPClient发送请求超时返回EOF错误DEBUG

    解决Goland中利用HTTPClient发送请求超时返回EOF错误DEBUG

    这篇文章主要介绍了解决Goland中利用HTTPClient发送请求超时返回EOF错误DEBUG,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang实现支付宝沙箱支付的方法步骤

    Golang实现支付宝沙箱支付的方法步骤

    本文主要介绍了Golang实现支付宝沙箱支付的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • 浅析Go语言中的栈和先进先出原则

    浅析Go语言中的栈和先进先出原则

    这篇文章主要来和大家探讨一样如何在Go语言中实现和使用堆栈,以及堆栈如何遵循先进先出 (FIFO) 原则,文中的示例代码简洁易懂,需要的可以参考一下
    2023-07-07
  • GO语言框架快速集成日志模块的操作方法

    GO语言框架快速集成日志模块的操作方法

    zap是一个可以在go项目中进行快速, 结构化且分级的日志记录包, git star数高达16.3k, Git 项目地址, 在各大公司项目中被广泛使用,这篇文章主要介绍了GO语言框架中如何快速集成日志模块,需要的朋友可以参考下
    2022-07-07

最新评论