Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/395.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在java中处理套接字_Java_Multithreading_Sockets_Thread Safety_Synchronized - Fatal编程技术网

在java中处理套接字

在java中处理套接字,java,multithreading,sockets,thread-safety,synchronized,Java,Multithreading,Sockets,Thread Safety,Synchronized,我正在用java为服务器创建一个套接字,在连接套接字之后,它会创建一个新线程,该线程可以访问套接字输入和输出流,然后当输入行进入时,该线程会阻塞并处理它们 我知道当输入流结束时,BufferedReader上的readln方法将返回null。这并不一定意味着套接字是关闭的,是吗?这是什么意思?因此,我想在套接字上运行close方法来很好地关闭它 我还了解,readln方法可以引发IOException,如果套接字当前处于阻塞状态,则在调用close方法后会引发IOException。还有什么时候

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

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

我还了解,
readln
方法可以引发IOException,如果套接字当前处于阻塞状态,则在调用
close
方法后会引发IOException。还有什么时候可以抛出这个?套接字在抛出后是否仍然打开,或者是否始终关闭并准备进行垃圾收集等

这是我目前拥有的代码,我真的不知道如何正确处理断开连接。目前,我认为如果在套接字等待线路时调用
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) {}
                }
            }
        }

    }
}

我主要想知道实现以下目标的最佳方法:

  • 编写一个连接到套接字并启动一个单独线程侦听输入的连接方法
  • 编写一个disconnect方法,该方法断开与套接字的连接,并终止正在侦听输入的线程
  • 处理与远程套接字的连接中断的情况
我已经通读了这本书,但在我看来,它并没有涵盖太多细节


谢谢

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


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

您走上了正确的道路。我不会使用“readline”,只使用原始阅读和“do stuff” 应限于构造接收数据的队列。同样,书面答复 应该是一个单独的线程,用于清空要发送的数据队列


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

当我说它可能会导致死锁时,我想我错了

将会发生的是:

  • 在.readLine()中调用disconnect(),
    in.readLine()
    阻塞
  • 已执行socket.close()
  • in.readline()引发IOException
  • 我当时认为SocketThread中的异常处理程序将调用disconnect,而disconnect正在等待异常完成。这并不重要,因为它们都是不同的线程,所以disconnect()中的代码将在SocketThread捕获异常时继续。SocketThread随后将调用disconnect(),但随后必须等待disconnect()的第一个实例完成。然后,disconnect()将再次执行,但会抛出NotConnectedException,该异常将被SocketThread捕获,不会发生任何事情。SocketThread将退出,这是所需的结果

    但是,我已经研究了,它还包含以下方法:

    • 关机输入()
    • 关机输出()
    shutdownInput()
    将结束EOF符号发送到输入流中,意思是.readline()中的
    in
    返回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) {}
                    }
                }
            }
    
        }
    }