Spring SSE应用Firefox接收首条数据后连接被关闭求助
我太懂这种卡了两周找不到突破口的感觉了!咱们来一步步拆解这个问题——你遇到的EofException本质是浏览器主动断开了连接,而Firefox在收到第一条数据就断,大概率是SSE协议规范没满足或者响应头/配置有问题,下面是几个你可以立刻排查的方向:
1. 先确认SSE响应头是否完全合规
SSE对响应头有严格要求,少一个都可能导致浏览器直接断开:
- 必须设置
Content-Type: text/event-stream,且不能带字符编码以外的额外参数 - 必须禁用缓存:
Cache-Control: no-cache或者Cache-Control: no-store - 绝对不能设置
Content-Length(SSE是流式传输,固定长度会让浏览器认为数据已传输完成) - 建议补充设置
Connection: keep-alive
你可以打开浏览器的Network面板,查看这条SSE请求的响应头,核对有没有不符合的地方。虽然Spring的SseEmitter默认会处理这些规则,但如果你手动修改过头信息,可能不小心破坏了规范。
2. 检查第一条数据的格式是否符合SSE规范
这是最容易踩坑的点!SSE的每条消息必须遵循固定格式:
data: 你的内容\n\n
哪怕你要发送JSON数据,也得用data:前缀包裹,比如:
data: {"id":1,"content":"hello"}\n\n
你现在调用的E1.send(value,MediaType.APPLICATION_JSON);存在一个隐藏问题:Spring的SseEmitter.send(Object, MediaType)会用消息转换器把对象转成JSON,但不会自动加上SSE要求的data:前缀和换行符!这就导致Firefox收到的是纯JSON,不是合法的SSE消息,直接判定连接异常断开了。
解决方法有两种:
- 手动把JSON包装成SSE格式字符串再发送:
String sseMessage = "data: " + objectMapper.writeValueAsString(value) + "\n\n"; E1.send(sseMessage, MediaType.TEXT_PLAIN); - 用Spring提供的
SseEventBuilder构建消息,它会自动处理格式:SseEventBuilder event = SseEmitter.event() .data(value, MediaType.APPLICATION_JSON); E1.send(event);
3. 排查Jetty的连接超时配置
如果头和格式都没问题,那可能是Jetty的idle超时设置太短。默认情况下Jetty的idle超时可能是30秒,如果第一条数据发送后没有后续心跳消息,Firefox可能会主动断开连接。你可以在Spring Boot配置里加长超时时间:
server.jetty.idle-timeout=300000 # 设为5分钟
同时建议定期发送心跳消息(比如每隔15秒发一条空的data: \n\n),保持连接活跃。
4. 排除Firefox本身的干扰
- 试试用隐私模式打开页面,排除浏览器插件拦截长连接的可能
- 检查
about:config里的网络设置:network.http.keep-alive.timeout:确保设得足够长(比如600秒)network.http.connection-timeout:不要设得太短
附:你提供的报错日志
org.eclipse.jetty.io.EofException at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:292) at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:429) at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:322) at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:372) at org.eclipse.jetty.server.HttpConnection$SendCallback.process(HttpConnection.java:750) at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241) at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:224) Caused by: java.io.IOException: An established connection was aborted by the software in your host machine at sun.nio.ch.SocketDispatcher.writev0(Native Method) at sun.nio.ch.SocketDispatcher.writev(SocketDispatcher.java:55) at sun.nio.ch.IOUtil.write(IOUtil.java:148) at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:503) at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:272) ... 29 more
内容的提问来源于stack exchange,提问作者sayan

