You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JPOS服务端如何在客户端建立连接时直接保存ISOSource,实现主动向客户端发送消息

JPOS服务端如何在客户端建立连接时直接保存ISOSource,实现主动向客户端发送消息

这个场景我之前做JPOS主动推送业务的时候也碰到过,你现在的写法确实只能在收到客户端的请求后才能捕获到ISOSource,想要客户端一连接上就拿到这个连接通道,得用上JPOS专门的SessionListener接口——它就是用来监听会话全生命周期事件的,比如连接建立、断开这些,刚好能解决你的问题。

下面给你一步步说怎么改:

1. 写一个会话监听器,捕获连接建立事件

首先我们要实现SessionListener接口,在客户端连接建立的时候(也就是sessionCreated方法触发时),把对应的ISOSource保存起来。这里要注意线程安全和多客户端的情况,别用单个static变量存,不然多个客户端连上来会覆盖,建议用ConcurrentHashMap来管理,按会话标识区分不同客户端:

@Slf4j
@Component
public class ClientConnectionListener implements SessionListener {
    // 用线程安全的Map管理多个客户端连接,key用会话名/客户端IP+端口当唯一标识
    private static final ConcurrentHashMap<String, ISOSource> clientConnections = new ConcurrentHashMap<>();

    @Override
    public void sessionCreated(Session session) {
        ISOSource source = session.getSource();
        String sessionKey = session.getName(); // 或者用session.getSocketAddress()拿客户端地址当key
        clientConnections.put(sessionKey, source);
        log.info("客户端连接建立,已保存会话:{}", sessionKey);
    }

    @Override
    public void sessionClosed(Session session) {
        String sessionKey = session.getName();
        clientConnections.remove(sessionKey);
        log.info("客户端连接断开,已清理会话:{}", sessionKey);
    }

    // 对外提供获取连接的方法,单客户端用这个拿活跃连接就行
    public ISOSource getActiveClientConnection() {
        return clientConnections.values().stream()
                .filter(ISOSource::isConnected)
                .findFirst()
                .orElse(null);
    }

    // 多客户端场景下,根据会话key获取指定连接
    public ISOSource getClientConnection(String sessionKey) {
        return clientConnections.get(sessionKey);
    }
}

2. 修改QServer配置,注册会话监听器

接下来要把这个监听器加到你的QServer配置里,让QServer在会话创建/销毁时通知我们的监听器:

<server class="org.jpos.q2.iso.QServer" logger="Q2" name="simulator_10000">
    <attr name="port" type="java.lang.Integer">10000</attr>
    <channel class="org.jpos.iso.channel.XMLChannel" logger="Q2" packager="org.jpos.iso.packager.XMLPackager"/>
    <request-listener class="com.example.jpos.services.HandleMsg" logger="Q2"/>
    <!-- 把我们的会话监听器加进来 -->
    <listener class="com.example.jpos.listeners.ClientConnectionListener" logger="Q2"/>
    <in>network-out</in> 
    <out>network-in</out>
</server>

3. 改造原有的HandleMsg服务,使用统一的连接管理

最后把你原来的HandleMsg里的static连接变量去掉,改成依赖我们的ClientConnectionListener来获取连接,这样不管客户端有没有先发请求,只要连接建立了,就能拿到ISOSource:

@Slf4j
@Service
public class HandleMsg implements ISORequestListener {

    private final ClientConnectionListener connectionListener;

    // 用构造注入的方式把监听器传进来(Spring Boot下会自动装配)
    public HandleMsg(ClientConnectionListener connectionListener) {
        this.connectionListener = connectionListener;
    }

    @Override
    public boolean process(ISOSource source, ISOMsg m) {
        try {
            if ("0800".equals(m.getMTI())) {
                ISOMsg r = (ISOMsg) m.clone();
                r.setResponseMTI();
                r.set(33, "Good Message");
                source.send(r);
                return true;
            }
        } catch (ISOException | IOException e) {
            log.error("处理请求出错:{}", e.getMessage());
        }
        return false;
    }

    public void sendMessageToService(String message) {
        // 拿活跃的客户端连接
        ISOSource clientConnection = connectionListener.getActiveClientConnection();
        try {
            if (Objects.isNull(clientConnection) || !clientConnection.isConnected()) {
                log.error("没有可用的客户端连接!");
                return;
            }
            ISOMsg serverPush = new ISOMsg();
            serverPush.setMTI("0210");
            serverPush.set(33, message);
            clientConnection.send(serverPush);
            log.info("主动推送消息给客户端成功:{}", message);
        } catch (IOException | ISOException e) {
            throw new RuntimeException("主动推送消息失败", e);
        }
    }
}

一些额外注意点

  • 如果你是多客户端连接的场景,记得在调用sendMessageToService时,要指定具体的sessionKey,用getClientConnection(sessionKey)来获取对应客户端的连接,别直接拿第一个活跃连接。
  • 要确保ClientConnectionListener被Spring正确扫描到(比如加了@Component注解,且所在包在Spring的扫描范围内),或者如果是纯Q2容器管理的话,要把它注册到Q2的组件里。
  • 别忘了处理连接断开的情况,我们在sessionClosed里已经清理了无效连接,避免后续调用时拿到已断开的连接。

备注:内容来源于stack exchange,提问作者shaxa 2000

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:52:57