0
votes

First, I'm not a developer (and I've been coding only for 2 weeks), so feel free to tell me I'm completely misunderstanding the thing (also, I wrote all of this for myself, so I'm sure it's super not cool) :). I want to learn and get it right, so I'm keen to listen to suggestions or complete rewrites.

I want to connect to a socket in non-blocking mode (I'm the client, not the server). I'll mainly need to read from it, but sometimes I'll need to write to it, too. The procedure is as follows:

  • Connect to socket
  • Send some initial requests to login to the server
  • Read from the socket
  • Sometimes, write some stuff (subscribe to certain information, for example)

My solution is as follows (I'm writing it in Java, because I've read it's a fast and good programming language, but I'm happy to change if required... hopefully not needed though!):

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();
        }
    }
}

I removed imports and other non-useful methods for convenience, and to keep the post short.

As you can see in my questions in the code (plus some added ones):

  • Reconnection: If I'm in non-blocking mode, how do I know that my request got sent successfully to the server
  • If I read from the socket and the message is "Reconnect to API", how can I make sure that happens before any other read / write?
  • Do I need to send the interestedOps over and over again?
  • I should only connect once to the socket. The fact that I'm non-blocking doesn't change that, right?

I've seen this could all be simplified using Netty or something, but I'm already bloated with so much stuff! :(

I hope my questions are clear. Let me know otherwise, please.

Thanks a lot.

1
This is a high level explanation about blocking and non-blocking sockets. Hope it helps someway: scottklement.com/rpg/socktut/nonblocking.html - VeryNiceArgumentException
@RaphaelMoita thanks a lot for that link. but I've already seen it. I does say there "Handling many sockets at once using select()", but I think that's not entirely true, is it? I think the good thing about select is to be able to read data quicker than creating a "spinning loop" or a sleep. Instead select will just "notify you" when there's data. So using select is good even if you're the client, and using one socket connection. Am I wrong? - Will
@SpiderPig do you think I could be using a blocking socket instead? The problem is that socket gives millions of records per second, so reads have to be very very fast, and writes shouldn't really block for a long time, otherwise I'll need to catch up with the previous data sent. Do you think Java is OK for this amount of data? I've seen C, but that looks like Matrix to me (even more than Java!) - Will
@SpiderPig you're my hero! Just last question then: ok, so I can remove the setBlocking call I make, so the read and writes will block, ok cool. And then how can I make it time out? Can't see any parameter I can send to read, but I can read in the socket I can do: public synchronized void setSoTimeout(int timeout) throws SocketException {. Would that work you think? PD: I need a developer in my life, there's so many things I don't know about all of this :( - Will
@SpiderPig thank you very very much man! You've been great help. I was trying to do something that just didn't make sense. In my case I can definitely use a blocking connection, which I just didn't know about :/. Internet is a bad source of information sometimes! I kept reading over here not to use a blocking connection :D. But now it makes perfect sense the different scenarios. - Will

1 Answers

0
votes

I was trying to do something that just didn't make sense. In my case I can definitely use a blocking connection, which I just didn't know about :/. Internet is a bad source of information sometimes! I kept reading over here not to use a blocking connection :D. But now it makes perfect sense the different scenarios. – Will