Avatar billede apocryphal Nybegynder
27. januar 2003 - 17:09 Der er 20 kommentarer og
1 løsning

SocketChannels

Hey.

Jeg forsøger at ombygge et eksempel fra www.javaworld.com (http://www.javaworld.com/javaworld/jw-09-2001/jw-0907-merlin.html), en webserver der anvender de nye java.nio pakker, fra webserver (altså stateless connections) til en server, hvor SocketChannelsne bliver holdt åbent, indtil aktivt lukket af enten server eller klient.

Denne kode:
protected void parseRequest(SelectionKey key) throws Exception
{
    SocketChannel incomingChannel = (SocketChannel) key.channel();
    Socket incomingSocket = incomingChannel.socket();

    try
    {
        int bytesRead = incomingChannel.read(readBuffer);
        readBuffer.flip();
        String result = asciiDecoder.decode(readBuffer).toString();
        readBuffer.clear();

        StringBuffer requestString = (StringBuffer)key.attachment();
        requestString.append(result);
       
        if (result.endsWith("\r\n"))
        {
            System.out.println("Received: " + requestString.toString());
        }
    }
    catch (IOException ioe)
    {
        ioe.printStackTrace();
    }
}

Hvordan sørger jeg for, at incomingChannel.read()-metoden, bliver clearet når jeg har læst dataen ud?

Jeg kan jo ikke bruge, at alle tidligere requests bliver hængende i dette buffer til evig tid ?!

I JavaDoc for SocketChannel kan jeg ikke umiddelbart finde noget.

Nogle der har erfaring med dette?

--
Jonas
Avatar billede arne_v Ekspert
27. januar 2003 - 19:19 #1
Jeg forstår ikke spørgsmålet !  (muligvis fordi jeg ikke er skrav til nio)

#Hvordan sørger jeg for, at incomingChannel.read()-metoden, bliver clearet
#når jeg har læst dataen ud?

#Jeg kan jo ikke bruge, at alle tidligere requests bliver hængende i dette
#buffer til evig tid ?!

Bliver readBuffer ikke enten overskrevet eller ryger ud af scope
til senere garbage colletion ?
Avatar billede apocryphal Nybegynder
27. januar 2003 - 20:05 #2
Arne_v, det håbede jeg også, men det er tilsyneladende ikke tilfældet.

Hvis jeg, med ovenstående kode, tilslutter en klient, og skriver "ABC123" og trykker ENTER, skriver Serveren, som det måtte forventes:
Received: ABC123

Man måtte nu gå ud fra, at ReadBuffer igen var tomt (på den ene eller anden måde), men skriver jeg igen "DEF456", Skriver serveren:
Received: ABC123
DEF456

Dette fortsætter så, skriver man DØ NU!, kommer teksten:
Avatar billede apocryphal Nybegynder
27. januar 2003 - 20:07 #3
Det var sært, hele teksten kom ikke med?!... Sært, se bort fra ovenstående kommentar, og regn med denne i stedet for:
-------
Arne_v, det håbede jeg også, men det er tilsyneladende ikke tilfældet.

Hvis jeg, med ovenstående kode, tilslutter en klient, og skriver "ABC123" og trykker ENTER, skriver Serveren, som det måtte forventes:
Received: ABC123

Man måtte nu gå ud fra, at ReadBuffer igen var tomt (på den ene eller anden måde), men skriver jeg igen "DEF456", Skriver serveren:
Received: ABC123
DEF456

Dette fortsætter så, skriver man DØ NU!, kommer teksten:
Received: ABC123
DEF456
DØ NU!

Jeg så gerne dette ikke var tilfældet :/

--
Jonas
Avatar billede arne_v Ekspert
27. januar 2003 - 20:22 #4
Er det et readBuffer problem ?

Jeg ville primært mistænke:

requestString.append(result);
Avatar billede apocryphal Nybegynder
27. januar 2003 - 21:32 #5
Jeg kan desværre ikke prøve, da kildekoden ligger på en maskine der står på arbejdet.

Hvorfor skulle den være problemet?
-> Bare så jeg lige kan lære det :)

--
Jonas
Avatar billede arne_v Ekspert
28. januar 2003 - 07:30 #6
Altså jeg *ved det ikke* - jeg *tror måske* at det der sker er:

StringBuffer requestString = (StringBuffer)key.attachment();
requestString.append(result);

betyder:
  hent den streng der er tilknyttet key
  append det indlæste i enden af den eksisterende streng
Avatar billede apocryphal Nybegynder
28. januar 2003 - 10:46 #7
Arne_v, ja ok... Det lyder logisk nok.

Det bekymre mig bare, at den tidligere data bliver hængende i systemet. Det er ikke sjovt hvis den bibeholder alt data fra alle måske 3000 (hypotese) connections i RAMene?

--
Jonas
Avatar billede arne_v Ekspert
28. januar 2003 - 10:50 #8
1)  Du kan nemt teste hypotesen med:

        int bytesRead = incomingChannel.read(readBuffer);
        readBuffer.flip();
        String result = asciiDecoder.decode(readBuffer).toString();

System.out.println("read and decode got " + result);

        readBuffer.clear();

        StringBuffer requestString = (StringBuffer)key.attachment();
        requestString.append(result);
       
        if (result.endsWith("\r\n"))
        {
            System.out.println("Received: " + requestString.toString());
        }
Avatar billede arne_v Ekspert
28. januar 2003 - 10:52 #9
Og hvis du laver en:
  key.attach(null);
på et passende sted i koden (=når du er færdig med en request og er klar
til næste), så skulle du starte forfra !
Avatar billede arne_v Ekspert
28. januar 2003 - 11:54 #10
Eller:
  key.attach("");
hvis det apsser bedre.
Avatar billede apocryphal Nybegynder
28. januar 2003 - 13:27 #11
Det viste sig ikke helt at være det der var løsningen (dog tæt på):
protected void parseRequest(SelectionKey key) throws Exception
{
    SocketChannel incomingChannel = (SocketChannel) key.channel();
    Socket incomingSocket = incomingChannel.socket();

    try
    {
        int bytesRead = incomingChannel.read(readBuffer);
        readBuffer.flip();
        String result = asciiDecoder.decode(readBuffer).toString();
        readBuffer.clear();
       
        StringBuffer requestString = (StringBuffer)key.attachment();
        requestString.append(result);
       
        if (result.endsWith("\r\n"))
        {
            System.out.println("Received: " + requestString.toString());
            key.attach(new StringBuffer());
        }
        else if(result.length() <= 0)
        {
            System.out.println("Connection closed!");
            incomingChannel.close();
            key.cancel();
        }
    }
    catch (IOException ioe)
    {
        ioe.printStackTrace();
    }
}

Tilsyneladende (det skal jeg vidst lige undersøge!), er attechmentet et StringBuffer (eller noget der er kombatibelt med det?).

Dette attachment bliver ved med at være der, i hele SocketChannelens registration. Så den skal nulstilles når man er færdig med den.

Det virker logisk nok.

Iøvrigt ser nio pakkerne ud til på en meget fin måde, at tackle problemet med, at det ikke umiddelbart er til at vide, om en klient har lukket forbindelsen.

Et andet spørgsmål (det er længe siden jeg har haft fat i de basale ting i Java)... Abstract class. De kan kun extendes, og sker dette, skal den nedarvende klasse implementere alle metoder i den abstrakte klasse?

SocketChannel klassen er nemlig abstrakt (den bliver oprettet af en factory klasse - hedder det vidst?), ved navn ServerSocketChannel.

Jeg vil nemlig godt holde tjek på mine klienters data (et bruger Id), ved at lave en klasse (ClientChannel extends SocketChannel), men dette går jo ikke?, da jeg tilsyneladende ikke må nedarbejde, uden at overskrive ALLE metoder?

--
Jonas
Avatar billede arne_v Ekspert
28. januar 2003 - 13:35 #12
Du skal kun implementere alle abstrakte metoder når du extender en
abstrakt klasse.

(men det er nu også rigeligt for denne klasse)
Avatar billede apocryphal Nybegynder
28. januar 2003 - 13:40 #13
arne_v, dvs. det ikke er værd at extende SocketChannel?

--
Jonas
Avatar billede arne_v Ekspert
28. januar 2003 - 13:42 #14
Men lav en ClientChannel som:
  - indeholder en SocketChannel
  - indeholder dit ID
  - delegerer alle metode kald til dens SocketChannel
Avatar billede apocryphal Nybegynder
28. januar 2003 - 14:15 #15
Okey... Men SocketChannel extender selv en AbstractClass: AbstractSelectableChannel ?

Løsningen er altså, lave
public class ClientChannel extends AbstractSelectableChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel

Og lade ALLE metodekald, gennemtvunget abstrakt klasse og interfaces, gå direkte til en SocketChannel?

--
Jonas
Avatar billede arne_v Ekspert
28. januar 2003 - 14:33 #16
Det er løsningen, *hvis* du har brug for at din
klasse kan bruges som en AbstractSelectableChannel eller
nogle af de andre !

Men hvis du *ikke* behøver det, så er livet dejligt nemt med:

public class ClientChannel {
  private SocketChannel sockchan;

Men ud fra den original kode du postede, så ser det nu ud som
du behøver det.

Jeg kan bare ikke gennemskue om det kan undgåes (har som dagt ikke
selv brugt channels).
Avatar billede apocryphal Nybegynder
28. januar 2003 - 14:43 #17
Jeg tror desværre livet er pain in the ass denne gang.

Ideen med NIO pakkerne, er at en SocketChannel har en Selector registretet. Denne selector kan kun registere ting, der er "SelectableChannels", altså er af typen AbstractSelectableChannel.

Selectorens opgave, er at lytte (så vidt jeg kan forstå) efter I/O Events (i dette tilfælde, om der er modtaget noget data).

Alt sammen er nonblocking, så man kan spare mange Threads i store applikationer.

--
Jonas
Avatar billede arne_v Ekspert
28. januar 2003 - 14:58 #18
En anden mulighed er følgende:

attach og attachment metoderne er defineret med Object.

Du bruger dem lige nu med StringBuffer.

Kunne du bruge dem med et objekt som indeholde både din ID og
din StringBuffer ?

(det ville løse problemet på en meget meget nem måde)
Avatar billede arne_v Ekspert
29. januar 2003 - 19:52 #19
Kunne du bruge den ide til noget ?
Avatar billede apocryphal Nybegynder
29. januar 2003 - 21:49 #20
Ja, jeg nærlæste det lidt - det objekt der er som attatchment, er noget man bestemmer når man registere SocketChannelen til dens Selector - jeg har lavet en ClientConnection, som kan håndtere de sager - det giver en meget mere stilfuld struktur i programmet, end man kunne opnå ved det gamle socketsystem.

Desværre kan én selector dog kun have 63 channels registeret :/... Så jeg har måtte lave en "ReadSelectorPool", som kan håndtere en masse af dem.

Men det virker perfekt anyway.
63 klienter / tråd er ok.

Tak for hjælpen - jeg vil godt give dig point, hvis du vil ha' dem - bare send et svar :)

--
Jonas
Avatar billede arne_v Ekspert
29. januar 2003 - 22:36 #21
Svar !
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester