QuickFIX/J如何处理End Of Stream错误与会话过期后重连异常
问题根因说明
Disconnecting, End Of Stream encountered报错本质是QuickFIX/J I/O层检测到服务端主动关闭了TCP连接,但没有返回合法的FIX协议报文。你遇到的循环回调问题,是因为重连时携带的过期会话标识(SenderCompID、TargetCompID、历史MsgSeqNum等)被服务端判定为无效,直接断开连接,而initiator默认的自动重连机制又不断发起重试,最终形成触发重连→被服务端断连→回调onLogout→再次触发重连的循环。
代码捕获及处理方案
- 第一步:实现自定义
SessionStateListener捕获异常状态
QuickFIX/J w.3版本提供了SessionStateListener接口,可以直接监听会话层的异常事件,你可以在该接口的onException方法中捕获目标异常,同时完成会话清理和前端通知逻辑:
public class CustomSessionStateListener implements SessionStateListener { // 注入Spring WebSocket向UI推消息的工具类 private final SimpMessagingTemplate messagingTemplate; public CustomSessionStateListener(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; } @Override public void onConnect(SessionID sessionID) {} @Override public void onDisconnect(SessionID sessionID) {} @Override public void onLogon(SessionID sessionID) {} @Override public void onLogout(SessionID sessionID) {} @Override public void onRefresh(SessionID sessionID) {} @Override public void onException(SessionID sessionID, Throwable cause) { // 匹配目标异常场景 if (cause.getMessage().contains("End Of Stream encountered")) { Session expiredSession = Session.lookupSession(sessionID); // 停止自动重连,打断循环 expiredSession.getLogonSchedule().setReconnectInterval(0); // 清理过期会话的序列号、上下文数据 expiredSession.reset(); // 向Angular前端推送会话过期提示 messagingTemplate.convertAndSend("/topic/fix/session-notice", "FIX会话已过期,请重新发起登录"); } } }
- 第二步:将自定义监听器注册到Initiator实例
在SpringBoot初始化Initiator的配置代码中,绑定你实现的状态监听器:
@Bean public SocketInitiator socketInitiator(Application fixApplication, MessageStoreFactory storeFactory, SessionSettings settings, LogFactory logFactory, MessageFactory messageFactory, SimpMessagingTemplate messagingTemplate) throws ConfigError { SocketInitiator initiator = new SocketInitiator(fixApplication, storeFactory, settings, logFactory, messageFactory); // 注册自定义状态监听器 initiator.addSessionStateListener(new CustomSessionStateListener(messagingTemplate)); return initiator; }
- 第三步:在
toAdmin回调中增加重连校验toAdmin被反复调用是因为每次重连都会触发Logon报文拼装,你可以在这里增加校验逻辑,阻止无效Logon报文发送:
@Override public void toAdmin(Message message, SessionID sessionID) throws DoNotSend { if (message instanceof Logon) { Session session = Session.lookupSession(sessionID); // 判断当前会话是否已经被标记为停用/过期 if (!session.isEnabled() || (session.getReceivedLogoutReason() != null && session.getReceivedLogoutReason().contains("会话停用"))) { // 抛出异常直接取消本次报文发送 throw new DoNotSend(); } } }
注意事项
不要在onLogout回调中执行会话重启、重连类逻辑,该操作会直接触发自动重连机制,加剧循环问题。如果不需要默认自动重连能力,可以直接在FIX配置文件中将ReconnectInterval参数设置为0,完全由业务层控制重连时机。
内容的提问来源于stack exchange,提问作者S Shah
相关产品推荐
相关产品推荐

