Golang jsonrpc2服务器在哪里监听?

vbkedwbf  于 2023-08-01  发布在  Go
关注(0)|答案(2)|浏览(142)

我想在golang中创建一个简单的LSP服务器,到目前为止,这是我写的代码:

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "sync"
  7. "github.com/sourcegraph/jsonrpc2"
  8. )
  9. type LSPServer struct {
  10. // The symmetric connection
  11. conn jsonrpc2.Conn
  12. // Check if the connection is available
  13. connMutex sync.Mutex
  14. // shutdown
  15. shutdown bool
  16. }
  17. func NewLSPServer() *LSPServer {
  18. return &LSPServer{}
  19. }
  20. func (s *LSPServer) Initialize(ctx context.Context) error {
  21. // to implement
  22. return nil
  23. }
  24. func (s *LSPServer) Handle(context.Context, *jsonrpc2.Conn, *jsonrpc2.Request) (result interface{}, err error) {
  25. fmt.Println("Handling request...")
  26. // to implement
  27. return nil, nil
  28. }
  29. func (s *LSPServer) Serve(ctx context.Context) {
  30. fmt.Println("Starting LSP server...")
  31. // what port is this server listening on?
  32. // it is listening on port 4389
  33. // Create a new jsonrpc2 stream server
  34. handler := jsonrpc2.HandlerWithError(s.Handle)
  35. // Create a new jsonrpc2 stream server
  36. <-jsonrpc2.NewConn(
  37. context.Background(),
  38. jsonrpc2.NewBufferedStream(os.Stdin, jsonrpc2.VSCodeObjectCodec{}),
  39. handler).DisconnectNotify()
  40. }
  41. func main() {
  42. // Create a new LSP server
  43. server := NewLSPServer()
  44. server.Serve(context.Background())
  45. }

字符串
它可以运行,但我不知道它在哪个端口上运行,也不知道如何用客户端调用它。有人有主意吗?
我想应该是4389端口,但不是那个
我用这个脚本测试:

  1. import json
  2. import requests
  3. def rpc_call(url, method, args):
  4. headers = {'content-type': 'application/json'}
  5. payload = {
  6. "method": method,
  7. "params": [args],
  8. "jsonrpc": "2.0",
  9. "id": 1,
  10. }
  11. response = requests.post(url, data=json.dumps(payload), headers=headers).json()
  12. return response['result']
  13. url = 'http://localhost:4389/'
  14. emailArgs = {'To': 'demo@example.com','Subject': 'Hello', 'Content': 'Hi!!!'}
  15. smsArgs = {'Number': '381641234567', 'Content': 'Sms!!!'}
  16. print(rpc_call(url, 'email.SendEmail', emailArgs))
  17. print(rpc_call(url, 'sms.SendSMS', smsArgs))


我认为这是正确的,因为我把这个客户端从另一个stackoverflow问题

k5ifujac

k5ifujac1#

我看到:

  1. HandlerWithError(s.Handle)
  2. // Create a new jsonrpc2 stream server
  3. <-jsonrpc2.NewConn(
  4. context.Background(),
  5. jsonrpc2.NewBufferedStream(os.Stdin, jsonrpc2.VSCodeObjectCodec{}),
  6. handler).DisconnectNotify()
  7. }

字符串
这意味着你的代码是在标准输入和输出(stdin/stdout)上使用JSON-RPC,而不是在网络连接上。
当您使用os.Stdin作为jsonrpc2.NewBufferedStream的参数时,您指定输入应该来自运行服务器的进程的标准输入。并且响应将被发送到标准输出。
因此,服务器不侦听任何网络端口。它与直接发送到其标准输入和输出的数据进行交互。这通常用于进程间通信,例如,当您希望一个进程调用服务器进程并接收响应时。
例如,参见“Go: bidirectional communication with another process?”或davidelorenzoli/stdin-stdout-ipc
如果你想让你的JSON-RPC服务器监听一个网络端口,你需要在Go中使用net package设置一个网络连接。您还需要修改客户端脚本,以便将其请求发送到正确的网络端口,而不是将HTTP请求发送到URL。

  1. package main
  2. import (
  3. "context"
  4. "net"
  5. "log"
  6. "sync"
  7. "github.com/sourcegraph/jsonrpc2"
  8. )
  9. type LSPServer struct {
  10. // The symmetric connection
  11. conn jsonrpc2.Conn
  12. // Check if the connection is available
  13. connMutex sync.Mutex
  14. // shutdown
  15. shutdown bool
  16. }
  17. func NewLSPServer() *LSPServer {
  18. return &LSPServer{}
  19. }
  20. func (s *LSPServer) Initialize(ctx context.Context) error {
  21. // Initialize here if needed
  22. return nil
  23. }
  24. func (s *LSPServer) Handle(context.Context, *jsonrpc2.Conn, *jsonrpc2.Request) (result interface{}, err error) {
  25. fmt.Println("Handling request...")
  26. // Handle something
  27. return nil, nil
  28. }
  29. func (s *LSPServer) Serve(ctx context.Context) {
  30. fmt.Println("Starting LSP server...")
  31. // Listen on TCP port 4389 on all available unicast and
  32. // anycast IP addresses of the local system.
  33. l, err := net.Listen("tcp", "localhost:4389")
  34. if err != nil {
  35. log.Fatal(err)
  36. }
  37. defer l.Close()
  38. for {
  39. // Wait for a connection.
  40. conn, err := l.Accept()
  41. if err != nil {
  42. log.Fatal(err)
  43. }
  44. // Handle the connection in a new goroutine.
  45. go func(c net.Conn) {
  46. // Create a new jsonrpc2 stream server
  47. handler := jsonrpc2.HandlerWithError(s.Handle)
  48. <-jsonrpc2.NewConn(
  49. ctx,
  50. jsonrpc2.NewBufferedStream(c, jsonrpc2.VSCodeObjectCodec{}),
  51. handler).DisconnectNotify()
  52. c.Close()
  53. }(conn)
  54. }
  55. }
  56. func main() {
  57. // Create a new LSP server
  58. server := NewLSPServer()
  59. go server.Serve(context.Background()) // run Serve in a separate goroutine
  60. select {} // wait forever
  61. }


这是一个基本示例,其中Serve方法创建了一个TCP侦听器,该侦听器侦听本地主机的端口4389。然后,它进入一个循环,等待连接,当它得到一个连接时,它启动一个新的goroutine来使用JSON-RPC服务器处理该连接。
在客户端,您需要打开到服务器的TCP连接,将JSON-RPC请求写入该连接,然后读取响应。
您不能像在Python脚本中那样使用requests library,因为它用于HTTP请求,而不是原始TCP连接。
您需要使用Python中的socket library或客户端语言中的类似库来创建TCP连接并通过它发送/接收数据。
但请记住,LSP (Language Server Protocol)是通过stdin/stdout而不是网络套接字操作的。
这是因为LSP服务器通常由编辑器/IDE作为子进程启动,并通过这些通道直接通信。因此,根据您的用例,原始的stdin/stdout方法可能更合适。

展开查看全部
pvcm50d1

pvcm50d12#

这是对@VonC优秀答案的补充。这个答案提供了一个stdioReadWriteCloser来使原始的stdin/stdout方法工作。请先阅读@VonC的回答。
在原始问题中构建源代码:

  1. go build -o lspserver .

字符串
然后将此消息发送到lspserverstdin

  1. echo 'Content-Length: 70\r\n\r\n{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}' | ./lspserver
  2. Starting LSP server...
  3. Handling request...
  4. 2023/07/16 16:16:37 jsonrpc2 handler: sending response 2: write /dev/stdin: bad file descriptor


(Note:Content-Length: %d\r\n\r\n是VSCodeObjectCodec所必需的)
注意错误消息。它尝试写入stdin。这是不正确的。我们必须从stdinstdout创建一个io.ReadWriteCloser,如下所示:

  1. type stdioReadWriteCloser struct{}
  2. var _ io.ReadWriteCloser = (*stdioReadWriteCloser)(nil)
  3. func (c stdioReadWriteCloser) Read(p []byte) (n int, err error) {
  4. return os.Stdin.Read(p)
  5. }
  6. func (c stdioReadWriteCloser) Write(p []byte) (n int, err error) {
  7. return os.Stdout.Write(p)
  8. }
  9. func (c stdioReadWriteCloser) Close() error {
  10. return nil
  11. }


使用stdioReadWriteCloser如下:

  1. <-jsonrpc2.NewConn(
  2. context.Background(),
  3. jsonrpc2.NewBufferedStream(stdioReadWriteCloser{}, jsonrpc2.VSCodeObjectCodec{}),
  4. handler).DisconnectNotify()


构建lspserver并重试:

  1. echo 'Content-Length: 70\r\n\r\n{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 2}' | ./lspserver
  2. Starting LSP server...
  3. Handling request...
  4. Content-Length: 38
  5. {"id":2,"result":null,"jsonrpc":"2.0"}


现在它正如预期的那样工作!
奇怪的是,github.com/sourcegraph/jsonrpc2没有提供这样一个开箱即用的 Package 器。

展开查看全部

相关问题