提问人:Matteo Possamai 提问时间:7/16/2023 更新时间:7/16/2023 访问量:129
Golang jsonrpc2 服务器 listending 在哪里?
Golang jsonrpc2 server where is it listending?
问:
我想在 golang 中创建一个简单的 LSP 服务器,到目前为止,这是我编写的代码:
package main
import (
"context"
"fmt"
"os"
"sync"
"github.com/sourcegraph/jsonrpc2"
)
type LSPServer struct {
// The symmetric connection
conn jsonrpc2.Conn
// Check if the connection is available
connMutex sync.Mutex
// shutdown
shutdown bool
}
func NewLSPServer() *LSPServer {
return &LSPServer{}
}
func (s *LSPServer) Initialize(ctx context.Context) error {
// to implement
return nil
}
func (s *LSPServer) Handle(context.Context, *jsonrpc2.Conn, *jsonrpc2.Request) (result interface{}, err error) {
fmt.Println("Handling request...")
// to implement
return nil, nil
}
func (s *LSPServer) Serve(ctx context.Context) {
fmt.Println("Starting LSP server...")
// what port is this server listening on?
// it is listening on port 4389
// Create a new jsonrpc2 stream server
handler := jsonrpc2.HandlerWithError(s.Handle)
// Create a new jsonrpc2 stream server
<-jsonrpc2.NewConn(
context.Background(),
jsonrpc2.NewBufferedStream(os.Stdin, jsonrpc2.VSCodeObjectCodec{}),
handler).DisconnectNotify()
}
func main() {
// Create a new LSP server
server := NewLSPServer()
server.Serve(context.Background())
}
它运行,但我不知道它在哪个端口上运行,或者通常如何使用客户端调用它。有人有什么想法吗?
我认为应该是端口 4389,但不是那个
我正在用这个脚本进行测试:
import json
import requests
def rpc_call(url, method, args):
headers = {'content-type': 'application/json'}
payload = {
"method": method,
"params": [args],
"jsonrpc": "2.0",
"id": 1,
}
response = requests.post(url, data=json.dumps(payload), headers=headers).json()
return response['result']
url = 'http://localhost:4389/'
emailArgs = {'To': '[email protected]','Subject': 'Hello', 'Content': 'Hi!!!'}
smsArgs = {'Number': '381641234567', 'Content': 'Sms!!!'}
print(rpc_call(url, 'email.SendEmail', emailArgs))
print(rpc_call(url, 'sms.SendSMS', smsArgs))
我认为这是正确的,因为我从另一个 stackoverflow 问题中获取了这个客户端
答:
明白了:
HandlerWithError(s.Handle)
// Create a new jsonrpc2 stream server
<-jsonrpc2.NewConn(
context.Background(),
jsonrpc2.NewBufferedStream(os.Stdin, jsonrpc2.VSCodeObjectCodec{}),
handler).DisconnectNotify()
}
这意味着您的代码在标准输入和输出 (stdin/stdout) 上使用 JSON-RPC,而不是通过网络连接。
当您用作 jsonrpc2 的参数时。NewBufferedStream
,则指定输入应来自运行服务器的进程的标准输入。响应将被发送到标准输出。os.Stdin
因此,服务器未侦听任何网络端口。它与直接发送到其标准输入和输出的数据进行交互。这通常用于进程间通信,例如,当您希望一个进程调用服务器进程并接收响应时。
例如,请参阅“Go: bidirectional communication with another process?”或 davidelorenzoli/stdin-stdout-ipc
。
如果您希望 JSON-RPC 服务器侦听网络端口,则需要使用 net
包在 Go 中设置网络连接。您还需要修改客户端脚本,将其请求发送到正确的网络端口,而不是向 URL 发送 HTTP 请求。
package main
import (
"context"
"net"
"log"
"sync"
"github.com/sourcegraph/jsonrpc2"
)
type LSPServer struct {
// The symmetric connection
conn jsonrpc2.Conn
// Check if the connection is available
connMutex sync.Mutex
// shutdown
shutdown bool
}
func NewLSPServer() *LSPServer {
return &LSPServer{}
}
func (s *LSPServer) Initialize(ctx context.Context) error {
// Initialize here if needed
return nil
}
func (s *LSPServer) Handle(context.Context, *jsonrpc2.Conn, *jsonrpc2.Request) (result interface{}, err error) {
fmt.Println("Handling request...")
// Handle something
return nil, nil
}
func (s *LSPServer) Serve(ctx context.Context) {
fmt.Println("Starting LSP server...")
// Listen on TCP port 4389 on all available unicast and
// anycast IP addresses of the local system.
l, err := net.Listen("tcp", "localhost:4389")
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
// Wait for a connection.
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
// Handle the connection in a new goroutine.
go func(c net.Conn) {
// Create a new jsonrpc2 stream server
handler := jsonrpc2.HandlerWithError(s.Handle)
<-jsonrpc2.NewConn(
ctx,
jsonrpc2.NewBufferedStream(c, jsonrpc2.VSCodeObjectCodec{}),
handler).DisconnectNotify()
c.Close()
}(conn)
}
}
func main() {
// Create a new LSP server
server := NewLSPServer()
go server.Serve(context.Background()) // run Serve in a separate goroutine
select {} // wait forever
}
这是一个基本示例,其中该方法创建一个 TCP 侦听器,该侦听器侦听本地主机的端口 4389。然后,它进入一个循环,等待连接,当它获得连接时,它会启动一个新的 goroutine 来使用您的 JSON-RPC 服务器处理该连接。Serve
在客户端,您需要打开与服务器的 TCP 连接,将 JSON-RPC 请求写入该连接,然后读取响应。
您不能像在 Python 脚本中那样使用 requests
库,因为它用于 HTTP 请求,而不是原始 TCP 连接。
您需要使用 Python 中的套接字
库或客户端语言中的类似库来创建 TCP 连接并通过它发送/接收数据。
但请记住,LSP(语言服务器协议)通过 stdin/stdout 而不是网络套接字运行。
这是因为 LSP 服务器通常由编辑器/IDE 作为子进程启动,并直接通过这些通道进行通信。因此,根据您的用例,原始的 stdin/stdout 方法可能更合适。
这是对@VonC出色回答的补充。此答案提供了使原始 stdin/stdout 方法起作用的方法。请先阅读@VonC的回答。stdioReadWriteCloser
在原始问题中构建源代码:
go build -o lspserver .
然后将此消息发送到 的 :lspserver
stdin
echo 'Content-Length: 70\r\n\r\n{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}' | ./lspserver
Starting LSP server...
Handling request...
2023/07/16 16:16:37 jsonrpc2 handler: sending response 2: write /dev/stdin: bad file descriptor
(注意:VSCodeObjectCodec 需要Content-Length: %d\r\n\r\n
)
请注意错误消息。它尝试写入 。这是不正确的。我们必须创建一个 from,如下所示:stdin
io.ReadWriteCloser
stdin
stdout
type stdioReadWriteCloser struct{}
var _ io.ReadWriteCloser = (*stdioReadWriteCloser)(nil)
func (c stdioReadWriteCloser) Read(p []byte) (n int, err error) {
return os.Stdin.Read(p)
}
func (c stdioReadWriteCloser) Write(p []byte) (n int, err error) {
return os.Stdout.Write(p)
}
func (c stdioReadWriteCloser) Close() error {
return nil
}
并像这样使用:stdioReadWriteCloser
<-jsonrpc2.NewConn(
context.Background(),
jsonrpc2.NewBufferedStream(stdioReadWriteCloser{}, jsonrpc2.VSCodeObjectCodec{}),
handler).DisconnectNotify()
生成并重试:lspserver
echo 'Content-Length: 70\r\n\r\n{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 2}' | ./lspserver
Starting LSP server...
Handling request...
Content-Length: 38
{"id":2,"result":null,"jsonrpc":"2.0"}
现在它按预期工作!
奇怪的是,没有提供这样的开箱即用的包装器。github.com/sourcegraph/jsonrpc2
评论