说在前面
- 操作系统:win11 wsl2
- go-ethereum版本:1.15.8
关于json-rpc
server
- 定义方法
这里我们简单定义两个方法type 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 }
- 启动服务
func 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
假设将server端的func 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 }
RegisterName("calculator", calculator)
改为RegisterName("calculate", calculator)
panic: 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
- 例如,我们调用
得到的err = client.Call(&result, "calculate_div", 3, 0) if err != nil { panic(err) }
err
即为server端Div
方法返回的error
panic: divide by zero
server端调用client端
- 支持反向调用
- 在我们定义
server
端方法的时候,可以增加一个context参数type 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
端添加一个方法
然后在client端注册type CalcService struct{} func (s *CalcService) GetParam(ctx context.Context) int { return 88 }
在server端的Add方法中,我们将b的值改为从client端获取func 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 }
这样运行后的结果为:func (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 }
Result: 91
- 通过这种方式,我们可以在server端添加一个
Register
的方法,在client连接后,client主动调用Register
方法,这样我们就可以管理所有client了