说在前面
- 操作系统:win11 wsl2
- go-ethereum版本:1.15.8
关于json-rpc
server
-
定义方法
gotype CalculatorService struct{} func (s *CalculatorService) Add(a, b int) int { return a + b } func (s *CalculatorService) Div(a, b int) (int, error) { if b == 0 { return 0, errors.New("divide by zero") } return a / b, nil }
这里我们简单定义两个方法
-
启动服务
gofunc main() { calculator := new(CalculatorService) // 创建 RPC 服务器 rpcServer := rpc.NewServer() // 注册服务 if err := rpcServer.RegisterName("calculator", calculator); err != nil { panic(err) } // 启动 HTTP 服务器 println("WebSocket server listening on :8080") httpHandler := rpcServer.WebsocketHandler([]string{"*"}) http.Handle("/ws", httpHandler) http.ListenAndServe(":8080", nil) }
client
-
调用
在调用的时候,方法名需要指定为receiver_method
,例如我们要调用CalculatorService.Add
,那么调用名为calculator_add
,注意,这里的calculator
为server端注册时的RegisterName("calculator", calculator)
,而不是CalculatorService
gofunc main() { // 连接 WebSocket 服务端 client, err := rpc.DialWebsocket(context.Background(), "ws://192.168.1.6:8080/ws", "") if err != nil { panic(err) } defer client.Close() // 调用远程方法 var result int err = client.Call(&result, "calculator_add", 3, 5) if err != nil { panic(err) } fmt.Println("Result:", result) // 输出: Result: 8 }
假设将server端的
RegisterName("calculator", calculator)
改为RegisterName("calculate", calculator)
shellpanic: the method calculator_add does not exist/is not available
注意事项
返回值
-
server端的方法返回值只能有三种数量0,1,2
- 0,即无返回值
- 1,即自定义返回值或者一个
error
- 2,即自定义返回值+一个
error
-
这样,在client端调用的时候,
err = client.Call(&result, "calculator_add", 3, 5)
,result
对应自定义返回值,err
对应返回的error
-
例如,我们调用
goerr = client.Call(&result, "calculate_div", 3, 0) if err != nil { panic(err) }
得到的
err
即为server端Div
方法返回的error
shellpanic: divide by zero
server端调用client端
-
支持反向调用
-
在我们定义
server
端方法的时候,可以增加一个context参数gotype CalculatorService struct{} func (s *CalculatorService) Add(ctx context.Context, a, b int) int { return a + b } func (s *CalculatorService) Div(ctx context.Context, a, b int) (int, error) { if b == 0 { return 0, errors.New("divide by zero") } return a / b, nil }
-
这样我们就可以获取到
client
,进而调用其方法,例如,我们在client
端添加一个方法gotype CalcService struct{} func (s *CalcService) GetParam(ctx context.Context) int { return 88 }
然后在client端注册
gofunc main() { // 连接 WebSocket 服务端 client, err := rpc.DialWebsocket(context.Background(), "ws://192.168.1.6:8080/ws", "") if err != nil { panic(err) } defer client.Close() calculator := new(CalcService) client.RegisterName("calc", calculator) // 调用远程方法 var result int err = client.Call(&result, "calculate_add", 3, 0) if err != nil { panic(err) } fmt.Println("Result:", result) // 输出: Result: 8 }
在server端的Add方法中,我们将b的值改为从client端获取
shellfunc (s *CalcService) Add(ctx context.Context, a, b int) int { client, ok := rpc.ClientFromContext(ctx) if ok { client.Call(&b, "calc_getParam") } return a + b }
这样运行后的结果为:
shellResult: 91
-
通过这种方式,我们可以在server端添加一个
Register
的方法,在client连接后,client主动调用Register
方法,这样我们就可以管理所有client了