【问题标题】:Concurrent Threads Reading a Socket读取套接字的并发线程
【发布时间】:2014-07-20 20:08:58
【问题描述】:

我有一个简单的服务器-客户端套接字连接。我将所有数据封装在对象中,这些对象在套接字之间来回发送,通过 ObjectStreams 发送。

我创建了一个“HeartBeat”监视器,它在一个单独的线程中运行,服务器和客户端每 500 毫秒都会向后和向前发送一个 HeartBeat(空对象)以检查连接性,效果很好。但是,正因为如此,当我想在服务器和客户端之间发送其他数据时,它会与这些 HeartBeat 对象混在一起。

例如,我的服务器需要一个登录对象,但却得到一个实例 HeartBeat 的对象。

我的代码是一个简单的客户端/服务器设置,所以我认为没有必要发布他们的代码,但是,HeartBeat 代码如下:

private static final int HEARTBEAT_INTERVAL = 500;

private void addHeartBeatMonitor(final Socket socket) {
    this.heartBeatTimer = new Timer();
    this.heartBeatTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            try {
                ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
                os.writeObject(new HeartBeat());
                ObjectInputStream is = new ObjectInputStream(socket.getInputStream());
                if (!(is.readObject() instanceof HeartBeat)) { throw new IOException(); }
            } catch (IOException e) {
                LOG.info("Received disconnect from " + getClientSocket().getInetAddress());
                heartBeatTimer.cancel();
                if (clientSocket != null) {
                    try {
                        clientSocket.close();
                    } catch (IOException e1) {}
                }
            } catch (ClassNotFoundException e) {}
        }
    }, 0, HEARTBEAT_INTERVAL);
}

我的选择似乎如下:

  1. 放弃 HeartBeat 功能,尽管似乎没有其他可靠的方法来检查连接状态。
  2. 找到其他类型的 Socket 实现,它可以神奇地为我解决所有这些问题。
  3. 拥有一个同步的方法来监督对套接字的所有读取和写入,该方法会丢弃 HeartBeats 并将其他对象发送到它们应该存在的位置。
  4. 某种同步魔法。

提前感谢您的帮助!

编辑: 读取登录对象的代码(服务器端):

User result = null;
try {
    ObjectInputStream is = new ObjectInputStream(this.getInputStream());
    Login request = (Login) is.readObject(); ### ERROR ###
    result = this.mongoService.login(request);
    ObjectOutputStream os = new ObjectOutputStream(this.getOutputStream());
    os.writeObject(result);
} catch (IOException e) {
} catch (ClassNotFoundException e) {}
return result;

异常如下:

Exception in thread "Thread-0" java.lang.ClassCastException: model.HeartBeat cannot be cast to model.Login
    at socket.SocketServerWorker.login(SocketServerWorker.java:78)
    at socket.SocketServerWorker.<init>(SocketServerWorker.java:47)
    at socket.SocketServer$2.run(SocketServer.java:50)
    at java.lang.Thread.run(Thread.java:744)

【问题讨论】:

  • @Cristian 您当前的问题是一个更为普遍的问题的表现:您的服务器如何区分不同类型的请求?一种常见的方法是为每个请求标记自己,以便服务器可以在投射它刚刚读取的对象之前做出决定。一种方法是让每个请求都派生自提供标识符的基类,然后让服务器基于该标识符进行操作。另一种选择是根本不发送类,而是使用简单的基于 text/json/xml 的协议,再次使用“请求类型”字段。
  • 考虑只有一个线程不断地从套接字读取所有数据,并相应地调度响应。如果收到心跳,则处理它。如果收到登录响应,则调用一些事件回调。在另一个线程上的登录代码中,注册事件处理程序,发送您的请求,然后等待响应事件被触发。让单个阅读线程处理所有事情并将响应传递回对它们感兴趣的任何人。这样,请求线程独立等待响应线程查看他们感兴趣的响应。
  • 这段代码无论如何都不起作用,因为套接字上有多个对象流。我只需在第二个线程上发送心跳请求,在主线程正在使用的同一对象输出流上,通过适当的同步,并让主线程处理所有回复,包括心跳响应。它可以使用读取超时来检测心跳响应的缺失。
  • @JasonC 然后等待两个小时。套接字的存在以及其上没有读取超时和连接重置就足够了。我可能会完全摆脱心跳。
  • @EJP 我完全同意读取超时和连接重置在这里绰绰有余,并且心跳是多余的(当然,除非您希望在服务器出于监控目的而关闭时立即收到通知,即使您的连接处于空闲状态 - 但对于非管理客户端应用程序来说,这可能不是真正需要的)。

标签: java multithreading sockets


【解决方案1】:

考虑做这样的事情。我只是把它放在一起,所以它显然未经测试,但我相信你会明白的:

public class HeartBeatMonitor
{
    final Map<Class,Consumer> handlers = new HashMap<> ();
    final Socket sock;
    final ObjectInputStream is;
    final ObjectOutputStream os;

    public HeartBeatMonitor (final Socket sock)
    {
        try
        {
            this.sock = sock;
            this.is = new ObjectInputStream (sock.getInputStream ());
            this.os = new ObjectOutputStream (sock.getOutputStream ());
        }
        catch (final IOException e)
        {
            throw new RuntimeException (e);
        }
    }

    public <T> void setHandler (final Class<T> type, final Consumer<? super T> handler)
    {
        this.handlers.put (type, handler);
    }

    // This would be called in a loop
    void accept () throws ClassNotFoundException, IOException
    {
        final Object o = this.is.readObject ();
        final Consumer handler = this.handlers.get (o.getClass ());
        if (handler != null)
            handler.accept (o);
        // Else default handler?
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-12
    • 2015-03-18
    • 2018-03-27
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多