【问题标题】:Handling sockets in java在java中处理套接字
【发布时间】:2013-07-21 18:41:34
【问题描述】:

我正在用 java 创建一个到服务器的套接字,在连接套接字后,它会创建一个新线程,该线程可以访问套接字输入和输出流,然后该线程会在输入行进入时阻塞并处理它们。

我了解 BufferedReader 上的 readln 方法将在输入流结束时返回 null。这并不一定意味着套接字已关闭,不是吗?这是什么意思?所以我想在套接字上运行close 方法来很好地关闭它。

我还了解到readln 方法可以抛出 IOException,并且如果当前阻塞,则在套接字上调用 close 方法之后抛出该异常。这还能扔到什么时候?抛出此套接字后套接字是否仍处于打开状态,或者它是否始终处于关闭状态并准备好进行垃圾收集等。

这是我目前拥有的代码,我真的不知道如何正确处理断开连接。目前我认为如果在套接字等待一行时调用disconnect 方法,这可能会导致死锁,因为disconnect 将在套接字上调用close。这将在readLine 上抛出IOException,这将导致该catch 块再次调用disconnect

public class SocketManager {

    private Socket socket = null;
    private PrintWriter out = null;
    private BufferedReader in = null;

    private String ip;
    private int port;

    private Object socketLock = new Object();

    public SocketManager(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    public void connect() throws UnableToConnectException, AlreadyConnectedException {
        synchronized(socketLock) {
            if (socket == null || socket.isClosed()) {
                throw (new AlreadyConnectedException());
            }
            try {
                socket = new Socket(ip, port);
                out = new PrintWriter(socket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            } catch (IOException e) {
                throw (new UnableToConnectException());
            }
            new Thread(new SocketThread()).start();
        }
    }

    public void disconnect() throws NotConnectedException {
        synchronized(socketLock) {
            if (isConnected()) {
                throw (new NotConnectedException());
            }
            try {
                socket.close();
            } catch (IOException e) {}
        }
    }

    public boolean isConnected() {
        synchronized(socketLock) {
            return (socket != null && !socket.isClosed());
        }
    }

    private class SocketThread implements Runnable {

        @Override
        public void run() {
            String inputLine = null;
            try {
                while((inputLine = in.readLine()) != null) {
                    // do stuff
                }
                if (isConnected()) {
                    try {
                        disconnect();
                    } catch (NotConnectedException e) {}
                }
            } catch (IOException e) {
                // try and disconnect (if not already disconnected) and end thread
                if (isConnected()) {
                    try {
                        disconnect();
                    } catch (NotConnectedException e1) {}
                }
            }
        }

    }
}

我基本上想知道实现以下目标的最佳方法:

  • 编写连接到套接字的连接方法并启动一个单独的线程来侦听输入。
  • 编写断开与套接字断开连接并终止正在侦听输入的线程的断开方法。
  • 处理与远程套接字的连接断开的情况。

我已经阅读了java tutorial on sockets,但我认为它并没有真正详细介绍这些内容。

谢谢!

【问题讨论】:

    标签: java multithreading sockets thread-safety synchronized


    【解决方案1】:

    当我说它可能会以僵局告终时,我认为我错了。

    会发生什么:

    1. disconnect() 在 in.readLine() 阻塞时调用
    2. socket.close() 已执行。
    3. in.readline() 抛出 IOException。

    然后我在想 SocketThread 中的异常处理程序会调用断开连接,而断开连接正在等待该异常完成。没有关系,因为它们都是不同的线程,因此 disconnect() 中的代码将继续,而异常被 SocketThread 捕获。然后 SocketThread 将调用 disconnect() 但必须等到 disconnect() 的第一个实例完成。然后 disconnect() 将再次执行,但会抛出 NotConnectedException,这将被 SocketThread 捕获,并且什么也不会发生。 SocketThread 将退出,这就是想要的结果。

    但是我查看了socket class,它还包含这些方法:

    • 关机输入()
    • 关闭输出()

    shutdownInput() 将结束 EOF 符号发送到输入流中,这意味着 in.readline() 返回 null 并且循环干净地退出。 shutdownOutput() 发送 TCP 终止序列,通知服务器它正在断开连接。

    socket.close() 之前调用这两个更有意义,因为这意味着线程将很好地退出,而不是因为抛出具有更多开销的异常而退出。

    所以这是修改后的代码:

    public class SocketManager {
    
        private Socket socket = null;
        private PrintWriter out = null;
        private BufferedReader in = null;
    
        private String ip;
        private int port;
    
        private Object socketLock = new Object();
    
        public SocketManager(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }
    
        public void connect() throws UnableToConnectException, AlreadyConnectedException {
            synchronized(socketLock) {
                if (socket == null || socket.isClosed()) {
                    throw (new AlreadyConnectedException());
                }
                try {
                    socket = new Socket(ip, port);
                    out = new PrintWriter(socket.getOutputStream(), true);
                    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                } catch (IOException e) {
                    throw (new UnableToConnectException());
                }
                new Thread(new SocketThread()).start();
            }
        }
    
        public void disconnect() throws NotConnectedException {
            synchronized(socketLock) {
                if (isConnected()) {
                    throw (new NotConnectedException());
                }
                try {
                    socket.shutdownInput();
                } catch (IOException e) {}
                try {
                    socket.shutdownOutput();
                } catch (IOException e) {}
                try {
                    socket.close();
                } catch (IOException e) {}
            }
        }
    
        public boolean isConnected() {
            synchronized(socketLock) {
                return (socket != null && !socket.isClosed());
            }
        }
    
        private class SocketThread implements Runnable {
    
            @Override
            public void run() {
                String inputLine = null;
                try {
                    while((inputLine = in.readLine()) != null) {
                        // do stuff (probably in another thread)
                    }
    
                    // it will get here if socket.shutdownInput() has been called (in disconnect)
                    // or possibly when the server disconnects the clients
    
    
                    // if it is here as a result of socket.shutdownInput() in disconnect()
                    // then isConnected() will block until disconnect() finishes.
                    // then isConnected() will return false and the thread will terminate.
    
                    // if it ended up here because the server disconnected the client then
                    // isConnected() won't block and return true meaning that disconnect()
                    // will be called and the socket will be completely closed
    
                    if (isConnected()) {
                        try {
                            disconnect();
                        } catch (NotConnectedException e) {}
                    }
                } catch (IOException e) {
                    // try and disconnect (if not already disconnected) and end thread
                    if (isConnected()) {
                        try {
                            disconnect();
                        } catch (NotConnectedException e1) {}
                    }
                }
            }
    
        }
    }
    

    【讨论】:

      【解决方案2】:

      为了确保与套接字关联的所有资源都被释放,您必须在完成该套接字的工作时调用 close() 方法。 典型的 IO 异常处理模式是捕获它,然后尽最大努力清理调用 close() 方法的所有内容。

      因此,您唯一需要做的就是确保在每个套接字的生命周期内调用 close()。

      【讨论】:

        【解决方案3】:

        你在正确的轨道上。我不会使用“readline”,只使用原始读取和“do stuff” 应该仅限于构造接收数据的队列。同样写回复 应该是一个单独的线程,用于清空要发送的数据队列。

        尽管套接字保证了完整性,但还是会出错,您有时会收到没有意义的数据。 “读”和“写”下面有一堆东西,没有一个系统是完美的或没有错误的。在您的读写级别添加您自己的带有校验和的包装器,这样您就可以确保您收到了要发送的内容。

        【讨论】:

          猜你喜欢
          • 2012-08-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多