NIO: Frage zu Non Blocking Server & großen Request-Daten

Billie

Erfahrenes Mitglied
Hellas!

Ich experimentiere gerade etwas mit dem NIO-Package (nicht NIO2) und einer "Non Blocking" Client-/Server-Kommunikation.

Also ich habe jetzt folgendet gemacht:

Ich habe mir einen Server geschrieben, der einen internen Buffer von 512 Bytes verwendet. Der Client sendet aber 1.024 Bytes an den Server.

Von diesen Client starte ich ca. 100 Instanzen gleichzeitig.

Mir kommt es jetzt vor, als würde ich pro Request zwei SelectionKey's bekommen. Ich lese also 512 Bytes vom SocketChannel des SelectionKeys, der Channel liefert mir aber dann keine weiteren 512 Bytes und ich arbeite mit dem nächsten Key weiter - von einem anderen Client. Irgendwann bekomme ich dann wieder einen Key vom ersten Client, mit den zweiten 512 Bytes.

Bedeutet das ich muss Daten im Server zwischenspeichern? Woher weiß ich dann dass der Request vom Client jetzt komplett ist?

Folgend einmal meine Testprogramme:

Java:
public class NIOServer {

    private Selector selector = null;

    private ServerSocketChannel server = null;

    private NIOServer() {
        startServer();
    }

    private void startServer() {
        try {
            server = ServerSocketChannel.open();
            server.configureBlocking(false);
            server.socket().bind(new InetSocketAddress(1234));
            selector = Selector.open();
            SelectionKey sk = server.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                selector.select();
                final Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                while (keys.hasNext()) {
                    final SelectionKey selectedKey = keys.next();
                    keys.remove();
                    if (selectedKey.isAcceptable()) {
                        accept(selectedKey);
                    } else if (selectedKey.isReadable()) {
                        read(selectedKey);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (server != null) {
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void accept(final SelectionKey key) {
        final ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        try {
            final SocketChannel channel = serverChannel.accept();
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void read(final SelectionKey key) {
        try {
            final SocketChannel channel = (SocketChannel) key.channel();
            final ByteBuffer buffer = ByteBuffer.allocate(512);
            int length = -1;
            try {
                final StringBuilder sb = new StringBuilder();
                buffer.clear();
                while ((length = channel.read(buffer)) > 0) {
                    final byte[] b = new byte[length];
                    buffer.clear();
                    buffer.get(b, 0, length);
                    sb.append(new String(b, 0, length).trim());
                }
                System.out.println(sb);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (length == -1) {
                channel.close();
                key.cancel();
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        new NIOServer();
    }

}

Java:
public class NIOClient
    extends Thread {

    public void run() {
        SocketChannel client = null;
        try {
            client = SocketChannel.open();
            client.configureBlocking(false);
            client.connect(new InetSocketAddress(1234));
            Selector selector = Selector.open();
            SelectionKey clientKey = client.register(selector, SelectionKey.OP_CONNECT);
            while (selector.select(500L) > 0) {
                for (final Iterator<SelectionKey> i = selector.selectedKeys().iterator(); i.hasNext(); i
                    .remove()) {
                    final SelectionKey key = i.next();
                    final SocketChannel channel = (SocketChannel) key.channel();
                    if (channel.isConnectionPending()) {
                        channel.finishConnect();
                    }
                    final String s = StringUtils.rightPad(getName(), 512);
                    channel.write(ByteBuffer.wrap((s + s).getBytes()));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (client != null) {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new NIOClient().start();
        }
    }

}

Also, ein NIOClient sendet einen String mit 1.024 Bytes an den Server. Der String besteht aus dem Thread-Namen, wird auf rightPad auf 512 Bytes "gepadded" und zusammengesetzt - sind also 1.024 Bytes.

Am Server lese ich dann jeweils 512 Bytes. Und eigentlich würde ich mir eine Ausgabe wie folgt erwarten:

Code:
Thread-19Thread-19
Thread-85Thread-85
...

Die Ausgabe sieht aber eher so aus:

Code:
Thread-19
Thread-85
Thread-62
Thread-19

Wie kann ich hier also am Server den Request von einem Client zusammenfassen?
 
Zuletzt bearbeitet von einem Moderator:
O.K., es sieht vermutlich so aus als würde das Protokoll (zB HTTP) vorschreiben, wann der Server genügend Bytes gelesen hat. Ich müsste also vermutlich eine Art Protokoll definieren und sagen ein Client-Request muss 1.024 Bytes groß sein.

Als Folge ist ein Request vom Client der kleiner als 1.024 Bytes ist einfach ungültig und mehr Bytes als 1.024 werden vom Server nicht gelesen bzw. beginnt dann evtl. der zweite Request.

Dann wäre aber noch die Frage offen, wie kann ich einen Channel am besten zuordnen?

Also, ich lese die erste 512 Bytes von Client A. Weiß also, da ist noch eine Anfrage offen und leg die 512 Bytes ersteinmal ab. Dann kommt noch ein Client mit 512 Bytes daher.

Wocher weiß ich jetzt ob es sich bei den zweiten 512 Bytes um den letzten Teil von Client A handelt od. um einen neuen Request von zB Client B?

Könnte ich hier einfach über die IP-Adresse des Clients gehen? Dann hätte ich aber zB ein Problem, weil die 100 lokalen NIOClient's natürlich jeweils die selbe IP haben.

Vermutlich reduziert sich jetzt die Frage auf "Wie identifiziere ich einen Client eindeutig"?
 
O.K., Fehler beim Lesen vom Client-Channel im Server. Folgend die korrekte read-Methode:

Java:
    private void read(final SelectionKey key) {
        try {
            final SocketChannel channel = (SocketChannel) key.channel();
            final ByteBuffer buffer = ByteBuffer.allocate(512);
            int length = -1;
            try {
                final StringBuilder sb = new StringBuilder();
                while ((length = channel.read(buffer)) > 0) {
                    final byte[] b = new byte[length];
                    buffer.flip();
                    buffer.get(b, 0, length);
                    sb.append(new String(b, 0, length).trim());
                    buffer.clear();
                }
                System.out.println(sb);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (length == -1) {
                channel.close();
                key.cancel();
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
Zuletzt bearbeitet von einem Moderator:
Zurück