首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用java处理套接字

用java处理套接字
EN

Stack Overflow用户
提问于 2013-07-21 18:41:34
回答 3查看 2.1K关注 0票数 0

我在java中为服务器创建一个套接字,在套接字连接之后,它创建一个新线程,它可以访问套接字输入和输出流,然后这个线程在输入行传入时阻塞和处理。

我了解到,当输入流结束时,BufferedReader上的BufferedReader方法将返回null。这并不一定意味着套接字已经关闭,对吗?这是什么意思?因此,我希望在套接字上运行close方法,以便很好地关闭它。

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

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

代码语言:javascript
复制
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教程,但在我看来,它并没有详细介绍这些内容。

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-07-21 21:15:51

当我说这可能会导致僵局的时候,我认为我错了。

会发生的情况是:

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

当时我在想,SocketThread中的异常处理程序会调用断开连接,而断开连接则在等待异常完成。这并不重要,因为它们都是不同的线程,因此在SocketThread中捕获异常时,disconnect()中的代码将继续。然后,SocketThread将调用disconnect(),但随后必须等待disconnect()的第一个实例完成。然后断开连接()将再次执行,但会抛出NotConnectedException,这将在SocketThread中捕获,不会发生任何事情。SocketThread将退出,这是想要的结果。

但是,我已经查看了套接字类,它还包含以下方法:

  • shutdownInput()
  • shutdownOutput()

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

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

这是修改后的代码:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2013-07-21 18:50:17

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

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

票数 0
EN

Stack Overflow用户

发布于 2013-07-21 18:55:45

你走在正确的轨道上。我不会使用"readline",只使用原始读取,“做一些事情”应该被限制在构建一个接收到的数据队列。同样,写回复应该是一个单独的线程,它清空要发送的数据队列。

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17775624

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档