如何设置一个大猩猩/ WebSocket 连接来充当浏览器应用程序的 JSON-RPC 客户端?

5

我有一个前端应用程序,它连接到WebSocket并尝试提供json-rpc2以从服务器调用客户端。在使用“github.com/gorilla/websocket”编写的服务器应用程序中,创建*rpc.Client的最佳方法是什么?我尝试使用*websocket.ConnUnderlyingConn()jsonrpc2.NewClient(conn.UnderlyingConn()),但它不起作用。


这是可以实现的,我已经制作了这样一个客户端。但是我想避免与协议手动实现相关的样板代码,这在标准库中已经完美实现,因此我希望使用*rpc.Client代替。 - fpawel
一个好的建议,谢谢。 - fpawel
@ThunderCat,也许你是指ClientCodec - fpawel
那个怎么样?(https://gist.github.com/fpawel/ba3738ed6309d917530de4342c12ce4a)这看起来可以接受吗?我使用了protected ReadJSON / WriteJSON。 - fpawel
2个回答

3
我为NewClient提供了io.ReadWriteCloser的实现,将读/写操作转换为websocket操作,得益于powerman
package jsonrpc2ws

import (
    "github.com/powerman/rpc-codec/jsonrpc2"
    "github.com/gorilla/websocket"
    "encoding/json"
    "io"
    "sync"
)

func NewClient(ws *websocket.Conn) *jsonrpc2.Client {
    c := &client{
        ws:ws,
    }
    c.initRead()
    c.initWrite()
    return jsonrpc2.NewClient(c)
}

type client struct {
    w *io.PipeWriter
    r *io.PipeReader
    ws *websocket.Conn

    err error      // io error
    mu sync.Mutex  // protects err
}


func (x *client) error() (err error) {
    x.mu.Lock()
    err = x.err
    x.mu.Unlock()
    return
}

func (x *client) setError (err error) {
    x.mu.Lock()
    if x.err != nil {
        x.err = err
    }
    x.mu.Unlock()
}

func (x *client) initRead(){
    var w *io.PipeWriter
    x.r, w = io.Pipe()
    go func(){
        _,b,err := x.ws.ReadMessage()
        for  ; x.error() == nil && err == nil; _,b,err = x.ws.ReadMessage(){
            _,err = w.Write(b)
        }
        x.setError(err)
        w.Close()
    } ()
}

func (x *client) initWrite(){
    var r *io.PipeReader
    r,x.w = io.Pipe()
    go func(){
        dec := json.NewDecoder(r)
        var b json.RawMessage
        err := dec.Decode(&b)
        for  ; x.error() == nil &&  err == nil; err = dec.Decode(&b){
            err = x.ws.WriteMessage(websocket.TextMessage, b)
        }
        x.setError(err)
        r.Close()
    } ()
}

func (x *client) Write(p []byte) (int, error){
    if x.error() != nil {
        return 0, x.error()
    }
    return x.w.Write(p)
}

func (x *client) Read(p []byte) (int, error){
    if x.error() != nil {
        return 0, x.error()
    }
    return x.r.Read(p)
}

func (x *client) Close() error{
    x.ws.Close()
    x.w.Close()
    x.r.Close()
    return nil
}

1
尝试使用nhooyr.io/websocket包的Net.Conn包装器。

https://pkg.go.dev/nhooyr.io/websocket#NetConn

这是用于通过WebSockets隧道传输任意协议的库。虽然该库的少数用户需要此功能,但正确实现它很棘手,因此在库中提供了此功能。请参见https://github.com/nhooyr/websocket/issues/100以获取更多详细信息。
链接的问题包含更多细节。 golang x/net/websocket package缺少许多功能。不幸的是,gorilla toolkit已被存档并且未得到维护。 nhooyr/websocket包对我有用,实际上被golang包推荐

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接