LSP4J技术问题:Language Server方法调用无限阻塞无法完成
解决Java LSP客户端initialize方法无限等待的问题
我之前也碰到过类似的LSP客户端阻塞问题,结合你的代码和现象——服务器在VSCode里正常运行,但客户端请求没送达、日志无输出,大概率是通信流处理或线程启动时机出了问题,咱们一步步排查解决:
1. 先确保监听线程完全启动再发送请求
LSPLauncher.startListening()会启动后台线程处理和服务器的双向通信,但这个方法是异步执行的。如果在它还没初始化完成时就调用server.initialize().get(),请求可能根本无法被发送到服务器。
修改代码,等待监听线程就绪后再发送初始化请求:
Socket socket = new Socket("localhost", 6008); Launcher<LanguageServer> createClientLauncher = LSPLauncher.createClientLauncher(languageClient, socket.getInputStream(), socket.getOutputStream()); LanguageServer server = createClientLauncher.getRemoteProxy(); // 等待监听线程完全启动 createClientLauncher.startListening().join(); InitializeResult result = server.initialize(new InitializeParams()).get(); System.out.println("end");
2. 检查输出流的缓冲刷新问题
LSP基于JSON-RPC协议,需要确保请求内容被及时发送到服务器。默认的Socket输出流可能会缓冲数据,导致请求滞留在客户端缓冲区,根本没发出去。
可以把输出流包装成带自动刷新的缓冲流:
Socket socket = new Socket("localhost", 6008); // 包装输出流并确保自动刷新 OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream()); Launcher<LanguageServer> createClientLauncher = LSPLauncher.createClientLauncher(languageClient, socket.getInputStream(), outputStream); LanguageServer server = createClientLauncher.getRemoteProxy(); createClientLauncher.startListening().join(); // 发送初始化请求前手动刷新输出流(可选,但更保险) outputStream.flush(); InitializeResult result = server.initialize(new InitializeParams()).get(); System.out.println("end");
3. 补全InitializeParams的必填字段
虽然VSCode能正常和服务器通信,但你的客户端可能缺少InitializeParams里的关键必填字段,导致服务器即使收到请求也无法处理(不过这种情况服务器日志通常会有输出,但还是要排查):
InitializeParams params = new InitializeParams(); // 设置当前客户端进程ID,很多LSP服务器会校验这个 params.setProcessId(ProcessHandle.current().pid()); // 设置项目根路径,服务器需要这个来关联工作区 params.setRootUri(Paths.get("/your/project/root").toUri().toString()); // 设置客户端能力,至少要提供基础的文本同步能力 ClientCapabilities capabilities = new ClientCapabilities(); TextDocumentClientCapabilities textDocCaps = new TextDocumentClientCapabilities(); textDocCaps.setSynchronization(new TextDocumentSyncClientCapabilities()); capabilities.setTextDocument(textDocCaps); params.setCapabilities(capabilities); // 发送完整的初始化请求 InitializeResult result = server.initialize(params).get();
4. 手动调试Socket通信内容
如果以上方法都没用,可以给Socket的输入输出流加个包装,打印所有传输的内容,确认请求是否真的发送出去:
Socket socket = new Socket("localhost", 6008); // 包装输入流,打印服务器返回的内容 InputStream debugIn = new InputStream() { private final InputStream delegate = socket.getInputStream(); @Override public int read() throws IOException { int b = delegate.read(); if (b != -1) System.out.print((char) b); return b; } }; // 包装输出流,打印客户端发送的内容 OutputStream debugOut = new OutputStream() { private final OutputStream delegate = socket.getOutputStream(); @Override public void write(int b) throws IOException { System.out.print((char) b); delegate.write(b); } }; Launcher<LanguageServer> createClientLauncher = LSPLauncher.createClientLauncher(languageClient, debugIn, debugOut); // 后续代码不变
这样就能直观看到是否有JSON-RPC请求被发送,以及服务器是否有响应。
5. 确认服务器端口监听状态
最后再确认一下:你连接的localhost:6008确实是目标LSP服务器的监听端口。可以用以下命令检查:
- Windows:
netstat -ano | findstr :6008 - Linux/macOS:
lsof -i :6008
确保端口被正确监听,且客户端的Socket连接已经建立(状态为ESTABLISHED)。
内容的提问来源于stack exchange,提问作者kuldeep singh
相关产品推荐
相关产品推荐

