【问题标题】:Blocking a non-blocking socket client connection阻塞非阻塞套接字客户端连接
【发布时间】:2017-04-18 11:32:36
【问题描述】:

首先,我不是开发人员(而且我只编写了 2 周的代码),所以请随时告诉我我完全误解了这件事(另外,我为自己写了所有这些,所以我'确定它超级不酷):)。我想学习并把它做好,所以我很想听听建议或完成重写。

我想以非阻塞模式连接到套接字(我是客户端,而不是服务器)。我主要需要从中读取,但有时我也需要写入它。程序如下:

  • 连接到套接字
  • 发送一些初始请求以登录服务器
  • 从套接字读取
  • 有时,写一些东西(例如订阅某些信息)

我的解决方案如下(我用 Java 编写它,因为我读过它是一种快速且良好的编程语言,但如果需要,我很乐意更改......但希望不需要!):

public class SocketClient {

    public static void main(String[] args) {
        new Feed().init();
    }

    private boolean isSocketConnected() {
        return socket != null && socket.isConnected();
    }

    public void init() {
        try {
            if (isSocketConnected()) {
                // What here if I'm in non-blocking mode?
                // Would be good to know if the "close API" request succeeded
                // otherwise next time I won't be able to connect to their socket...

                sendCloseRequestToApi();
                socket.close();
            }

            run();
        } catch (Exception e) {
            if (isSocketConnected()) {
                // Same question as above...
                sendCloseRequestsToApi();
                socket.close();
            }
        }
    }

    public void run() throws IOException {
        System.out.println("Starting connection in blocking mode...");

        SocketChannel channel = SocketChannel.open();
        socket = channel.socket();
        socket.setReceiveBufferSize(RECEIVE_BUFFER_SIZE);
        socket.setSendBufferSize(SEND_BUFFER_SIZE);
        channel.connect(new InetSocketAddress("127.0.0.1", 2121));
        channel.finishConnect();

        System.out.println("Finished connecting in blocking mode");

        // Writes to the socket (user and password)
        initialiseTheApi();

        System.out.println("Sent API requests in blocking mode");
        System.out.println("Now we should probably go non-blocking (I guess)");

        channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
        selector = Selector.open();
        channel.configureBlocking(false);

        System.out.println("Selector created and switched to non-blocking mode...");

        long timeWithoutData = 0;
        boolean needsReconnection = false;
        while (!needsReconnection) {
            selector.select();
            Iterator < SelectionKey > keys = selector.selectedKeys().iterator();

            while (keys.hasNext()) {
                SelectionKey key = keys.next();
                keys.remove();

                if (!key.isValid()) {
                    continue;
                }

                if (key.isWritable()) {
                    // Execute write...
                    // What if I need to know the result to the write operation?
                }

                if (key.isReadable()) {
                    int dataRead = readDataFromSocket(buffer);

                    buffer.flip();
                    if (buffer.remaining() > 0) {
                        // I process the data read here,
                        // but sometimes the data sent is
                        // "reconnect to API". So I need to close
                        // the connection and start again.

                        // How can I do that if I'm in non-blocking mode?
                        // I mean, I need to make sure when I send that request
                        // (for reconnection).
                        // I need to know that the request got to the server and
                        // was processed OK before moving on and
                        // reading/writing again...
                    }

                    if (dataRead > -1) {
                        timeWithoutData = 0;
                    } else {
                        if (timeWithoutData > 0) {
                            long diffInMillis = System.currentTimeMillis() - timeWithoutData;
                            if (diffInMillis > 2000) {
                                System.out.println("Timeout or something? I need to reconnect I think");
                                needsReconnection = true;
                            }
                        } else {
                            timeWithoutData = System.currentTimeMillis();
                        }
                    }

                    // Do I even need this? Already did it before, right?
                    key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                }
            }
        }

        if (needsReconnection) {
            // We need full reconnection, go back up and reconnect
            init();
        }
    }
}

为了方便起见,我删除了导入和其他无用的方法,并保持帖子简短。

正如您在代码中的问题中看到的(以及一些添加的问题):

  • 重新连接:如果我处于非阻塞模式,我如何知道我的请求已成功发送到服务器
  • 如果我从套接字读取并且消息是“重新连接到 API”,我如何确保在任何其他读取/写入之前发生这种情况?
  • 我需要一遍又一遍地发送interestedOps 吗?
  • 我应该只连接一次到套接字。我不阻塞的事实并没有改变这一点,对吧?

我已经看到这一切都可以使用 Netty 或其他东西来简化,但我已经被太多东西弄得臃肿了! :(

我希望我的问题很清楚。否则请告诉我。

非常感谢。

【问题讨论】:

  • 这是关于阻塞和非阻塞套接字的高级解释。希望它有所帮助:scottklement.com/rpg/socktut/nonblocking.html
  • @RaphaelMoita 非常感谢该链接。但我已经看过了。我确实说过“使用 select() 一次处理多个套接字”,但我认为这并不完全正确,是吗?我认为 select 的好处是能够比创建“旋转循环”或睡眠更快地读取数据。相反,当有数据时,select 只会“通知你”。因此,即使您是客户端并且使用一个套接字连接,使用 select 也很好。我错了吗?
  • @SpiderPig 你认为我可以使用阻塞套接字吗?问题是套接字每秒提供数百万条记录,因此读取必须非常非常快,并且写入不应该真正阻塞很长时间,否则我需要赶上之前发送的数据。你认为 Java 可以处理这么多的数据吗?我见过 C,但对我来说它看起来像 Matrix(甚至比 Java 还要多!)
  • @SpiderPig 你是我的英雄!那么最后一个问题:好的,所以我可以删除我所做的setBlocking 调用,所以读写会阻塞,好的酷。然后我怎样才能让它超时?看不到我可以发送到read 的任何参数,但我可以在我可以执行的套接字中读取:public synchronized void setSoTimeout(int timeout) throws SocketException {。你认为这行得通吗? PD:我的生活中需要一个开发者,关于这一切我有很多事情我不知道:(
  • @SpiderPig 非常非常感谢你!你帮了大忙。我试图做一些没有意义的事情。就我而言,我绝对可以使用阻塞连接,我只是不知道:/。互联网有时是一个糟糕的信息来源!我一直在这里阅读不要使用阻塞连接:D。但现在它完全可以理解不同的场景。

标签: java sockets nio nonblocking


【解决方案1】:

我试图做一些没有意义的事情。就我而言,我绝对可以使用阻塞连接,我只是不知道:/。互联网有时是一个糟糕的信息来源!我一直在这里阅读不要使用阻塞连接:D。但现在它完全可以理解不同的场景。 ——会

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 2010-10-31
    • 2013-10-15
    • 2013-07-20
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多