Java实现MCP实践:SSE与StdIO对比与实现
1. MCP简介
MCP(Model Context Protocol)是一种模型上下文协议,用于在不同系统组件之间建立高效的通信机制。它主要解决了以下问题:
- 标准化通信:提供统一的通信接口规范
- 高效数据传输:优化了大数据量传输场景
- 多协议支持:支持包括SSE、StdIO等多种通信方式
- 上下文管理:维护请求-响应过程中的上下文状态
MCP在现代分布式系统和微服务架构中有着广泛应用,特别是在需要实时数据交互的场景中表现突出。
2. SSE与StdIO的区别
2.1 SSE (Server-Sent Events)
SSE是一种基于HTTP的服务器推送技术,主要特点包括:
- 单向通信:仅支持服务器向客户端的单向数据推送
- 长连接:保持HTTP连接开放以实现持续数据传输
- 事件驱动:基于事件模型,支持自定义事件类型
- 自动重连:内置连接中断后的自动重连机制
- 文本协议:默认使用text/event-stream格式
适用场景:
- 实时通知系统
- 股票行情推送
- 实时日志监控
- 需要服务器主动推送数据的场景
2.2 StdIO (标准输入输出)
StdIO是传统的进程间通信方式,主要特点包括:
- 双向通信:支持进程间的双向数据交换
- 同步模式:通常采用请求-响应模式
- 简单直接:基于标准输入输出流实现
- 无状态:不维护请求间的上下文关系
- 二进制安全:可以传输任意二进制数据
适用场景:
- 命令行工具交互
- 批处理任务
- 脚本程序调用
- 需要双向通信的简单场景
2.3 对比总结
| 特性 | SSE | StdIO |
|------------|----------------------|----------------------|
| 通信方向 | 单向(服务器→客户端) | 双向 |
| 协议 | HTTP | 标准流 |
| 连接方式 | 长连接 | 短连接 |
| 数据格式 | 文本(event-stream) | 任意格式 |
| 适用场景 | 实时推送 | 进程间通信 |
| 复杂度 | 较高 | 较低 |
3. Java实现示例
3.1 SSE服务端实现
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.ConcurrentLinkedQueue;
@WebServlet(urlPatterns = "/sse", asyncSupported = true)
public class SseServlet extends HttpServlet {
private static final ConcurrentLinkedQueue<AsyncContext> clients =
new ConcurrentLinkedQueue<>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 设置SSE相关头信息
resp.setContentType("text/event-stream");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Connection", "keep-alive");
// 启用异步处理
final AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout(0);
clients.add(asyncContext);
// 发送初始数据
sendEvent(asyncContext, "init", "Connection established");
}
private void sendEvent(AsyncContext context, String event, String data) {
try {
PrintWriter writer = context.getResponse().getWriter();
writer.write("event: " + event + "\n");
writer.write("data: " + data + "\n\n");
writer.flush();
} catch (IOException e) {
clients.remove(context);
}
}
// 模拟事件推送方法
public static void pushEvent(String event, String data) {
clients.forEach(ctx -> {
SseServlet servlet = (SseServlet) ctx.getRequest()
.getServletContext()
.getAttribute("sseServlet");
servlet.sendEvent(ctx, event, data);
});
}
}
3.2 SSE客户端实现
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class SseClient {
public static void main(String[] args) {
try {
URL url = new URL("http://localhost:8080/sse");
URLConnection connection = url.openConnection();
connection.setDoInput(true);
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("data:")) {
System.out.println("Received: " + line.substring(5).trim());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3 StdIO服务端实现
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class StdIOServer {
public static void main(String[] args) {
try {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(System.out);
out.println("Server started. Ready for input...");
out.flush();
String input;
while ((input = in.readLine()) != null) {
if ("exit".equalsIgnoreCase(input)) {
break;
}
// 处理输入并返回响应
String response = "Processed: " + input.toUpperCase();
out.println(response);
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.4 StdIO客户端实现
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class StdIOClient {
public static void main(String[] args) {
try {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(System.out);
out.println("Enter messages (type 'exit' to quit):");
out.flush();
String input;
while ((input = in.readLine()) != null) {
// 发送到服务端
System.out.println(input); // 实际中会通过管道连接到服务端
if ("exit".equalsIgnoreCase(input)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 总结
本文详细介绍了MCP协议的基本概念,重点对比了SSE和StdIO两种通信方式的特性和适用场景,并提供了完整的Java实现示例。在实际项目中,开发者可以根据具体需求选择合适的通信方式:
- 选择SSE:当需要服务器主动推送数据、要求实时性高、且客户端主要是Web应用时
- 选择StdIO:当需要简单的进程间通信、批处理任务或命令行工具开发时
通过合理使用这两种技术,可以构建出高效、可靠的系统通信架构。