Java 读取套接字的并发线程

Java 读取套接字的并发线程,java,multithreading,sockets,Java,Multithreading,Sockets,我有一个简单的服务器-客户端套接字连接。我将所有数据封装在对象中,这些对象通过ObjectStreams在套接字之间来回发送 我已经创建了一个“HeartBeat”监视器,它在一个单独的线程中运行,服务器和客户端每500毫秒都会前后发送一个HeartBeat(空对象),以检查连接,这非常有效。然而,正因为如此,当我想在服务器和客户端之间发送其他数据时,它与这些HeartBeat对象混淆了 例如,我的服务器需要一个登录对象,但却得到一个实例HeartBeat的对象 我的代码是一个简单的客户机/服务

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

我已经创建了一个“HeartBeat”监视器,它在一个单独的线程中运行,服务器和客户端每500毫秒都会前后发送一个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);
}
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)
我的选择似乎如下:

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);
}
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)
  • 放弃心跳功能,尽管似乎没有其他可靠的方法来检查连接状态
  • 找到一些其他类型的套接字实现,它将神奇地为我解决所有这些问题
  • 有一个同步的方法来监督对套接字的所有读写,该方法丢弃心跳并将其他对象发送到它们想要的地方
  • 某种同步魔法
  • 提前感谢您的帮助

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

    例外情况如下:

    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);
    }
    
    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)
    
    线程“thread-0”java.lang.ClassCastException中的异常:model.HeartBeat无法强制转换为model.Login 位于socket.SocketServerWorker.login(SocketServerWorker.java:78) 位于socket.SocketServerWorker.(SocketServerWorker.java:47) SocketServer$2.run(SocketServer.java:50) 运行(Thread.java:744)
    考虑这样做。我只是把它放在一起,所以它显然没有经过测试,但我相信你会明白的:

    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?
        }
    }
    
    公共类HeartBeatMonitor
    {
    final-Map handlers=newhashmap();
    最终插座插座;
    最终目标输入流是;
    最终对象输出流;
    公共心脏监护仪(最终插座插座)
    {
    尝试
    {
    this.sock=sock;
    this.is=newObjectInputStream(sock.getInputStream());
    this.os=newObjectOutputStream(sock.getOutputStream());
    }
    捕获(最终IOE例外)
    {
    抛出新的运行时异常(e);
    }
    }
    
    公共无效设置处理程序(最终课程类型,最终Consumer@Cristian您当前的问题是一个更为普遍的问题的表现:您的服务器如何区分不同类型的请求?一种常见的方法是每个请求标记自身,以便服务器可以在强制转换它刚刚读取的对象之前做出决定。一种方法是让每个请求重新quest派生自一个提供标识符的基类,然后服务器根据该基类进行操作。另一个选项是根本不发送类,而是使用一个简单的基于文本/json/xml的协议,同样使用“请求类型”一个线程不断地读取来自socket的所有数据,并相应地发送响应。如果接收到心跳,则处理它。如果接收到登录响应,调用一些事件回调。在另一个线程的登录代码中,登记事件处理程序,发送请求,并等待响应事件触发。。让单个读取线程处理所有事情,并将响应传递回对它们感兴趣的任何对象。这样,请求线程将独立地等待响应线程查看它们感兴趣的响应。此代码无论如何都不会工作,因为套接字上有多个对象流。我只发送heart在第二个线程上的EAT请求,在主线程正在使用的同一对象输出流上,通过适当的同步,让主线程处理包括心跳响应在内的所有响应。它可以使用读取超时来检测心跳响应的缺失。@JasonC并等待两个小时。套接字的存在和缺失读取超时和连接重置的数量足够了。我可能会完全消除心跳。@EJP我完全同意读取超时和连接重置在这里已经足够了,心跳是多余的(当然,除非您希望在服务器出于监视目的停机时立即收到通知,即使您的连接处于空闲状态——但对于非管理客户端应用程序来说,这可能不是真正必要的)。