/ Forside / Teknologi / Udvikling / Java / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
Java
#NavnPoint
molokyle 3688
Klaudi 855
strarup 740
Forvirret 660
gøgeungen 500
Teil 373
Stouenberg 360
vnc 360
pmbruun 341
10  mccracken 320
Avanceret: Problem med modtagelse af data ~
Fra : Daniel Jacobsen


Dato : 17-08-03 18:57

Hejsa,

Jeg har lavet en multiplayer-server der bygger på non-blocking, multiplexed
I/O ved brug af java.nio. Serveren har et sidste problem, jeg længe har
kæmpet med: XML-beskederne der modtages fra klienterne bliver ofte modtaget
i to dele.

F.eks. hvis vi har en besked: '<tag noget="andet" noget1="andet1"/>'
modtages i første omgang '<tag noget="an' og anden omgang 'det"
noget1="andet1"/>' hvilket selvfølgelig gør at indholdet ikke kan parses.
Her er lidt kode der viser hvordan jeg læser fra en SocketChannel (fra min
Client-klasse):

int readBytes = 0;
readBytes = channel.read(receiveBuf);
if (readBytes < 0)
throw new IOException("End of stream");
int length = receiveBuf.remaining();
receiveBuf.flip();
byte[] buf = new byte[readBytes];
receiveBuf.get(buf);
String content = new String(buf);
receiveBuf.clear();

receiveBuf er selvfølgelig en instans af ByteBuffer i Client-objektet
arbejdes så længe klienten lever på den samme buffer (1 buffer til at læse
fra og 1 til at modtage med). Jeg har prøvet to ting, som begge giver den
fejl blot i to afskygninger:

ByteBuffer -> non-direct (placeres i JVM): Hvis beskeden læses i to dele går
oftest et eller to tegn tabt, hvorfor de ikke kan samles og behandles som én
besked.

ByteBuffer -> direct (placeres udenfor JVM): Anden del af den opsplittede
besked modtages 90% af gangen slet ikke, men tilgengæld går der aldrig nogen
tegn tabt, hvis endelig anden del modtages.

Jeg vil meget gerne have løst dette problem. Kan du give nyttig information
får du del i de 60 point, men bidrager du med information der hjælper til
den totale løsning af problemet får du hellere end gerne 200 point udover!

Problemet forekommer i snit 1 gang i minuttet for hver aktiv klient.

// Daniel J.



 
 
Daniel Jacobsen (17-08-2003)
Kommentar
Fra : Daniel Jacobsen


Dato : 17-08-03 18:59

Sry fik ikke lige fjernet de ting om point, som jeg skrev på eksperten.dk ;)

// Daniel J.



Filip Larsen (17-08-2003)
Kommentar
Fra : Filip Larsen


Dato : 17-08-03 23:48

Daniel Jacobsen skrev

> Jeg har lavet en multiplayer-server der bygger på non-blocking,
multiplexed
> I/O ved brug af java.nio. Serveren har et sidste problem, jeg længe har
> kæmpet med: XML-beskederne der modtages fra klienterne bliver ofte
modtaget
> i to dele.

Jeg vil tro, at du oplever det normale fænomen hvor de underliggende
netværksprotokoller hakker din besked op i mindre stykker som modtages
separat. TCP/IP og sockets har ikke noget record-begreb, dvs. de opfatter
blot data som en strøm af data der kan skilles ad og samles på vilkårlige
steder. Applikationer må selv afgrænse records hvis det ønskes, hvilket
oftes gøres med længdeafgrænsing for binære data eller evt. terminator
symboler for tekstuel data.

I dit tilfælde (med overførsel af en xml-streng) ville jeg først sende
længden af strengen og derefter selve strengen (vær opmærksom på om det er
bytes eller tegn du vil tælle). Modtageren starter så modtagelse af næste
xml-streng med at læse længden og derefter, i en løkke, læse fra kanalen
indtil det korrekte antal tegn eller bytes er læst. Med omhu kan dette også
gøres non-blocked.



--
Filip Larsen



Daniel Jacobsen (18-08-2003)
Kommentar
Fra : Daniel Jacobsen


Dato : 18-08-03 08:33

> I dit tilfælde (med overførsel af en xml-streng) ville jeg først sende
> længden af strengen og derefter selve strengen (vær opmærksom på om det er
> bytes eller tegn du vil tælle). Modtageren starter så modtagelse af næste
> xml-streng med at læse længden og derefter, i en løkke, læse fra kanalen
> indtil det korrekte antal tegn eller bytes er læst. Med omhu kan dette
også
> gøres non-blocked.

Der er et lille problem: drift-klienten er flash som anvender en XMLSocket,
hvorfor det ikke er muligt at tilføje længden på beskeden (som skal kunne
aflæses før beskeden parses). Desuden sender flash altid \0 som termintator
på beskeder. Hvis der kommer en besked fra klienten modtager man bare aldrig
terminatoren - eller, den kommer ikke med når ByteBuffer'en aflæses.

Jeg fandt en måde at simulere problemet på, hvilket selvfølgelig gør det
nemmere at finde problemet, nemlig ved på min testklient ("normal"
stream-klient) at flushe output-streamen efter hvert enkelt sendt tegn; i
stedet for efter at hele besked er sendt til output-streamen. Det gør at
serveren oftest modtager beskeden opslittet.

Hvis jeg reagerer på parse-fejlen ved først at cleare bufferen
(ByteBuffer.clear())og derefter læse en ekstra gang lykkes det mig at
modtage præcis de manglende data. At dette faktisk er løsningen forudsætter,
at mit simulerede problem er det samme der opstår i driften.

// Daniel J.



Filip Larsen (21-08-2003)
Kommentar
Fra : Filip Larsen


Dato : 21-08-03 16:04

Daniel Jacobsen skrev

> Der er et lille problem: drift-klienten er flash som anvender en
XMLSocket,
> hvorfor det ikke er muligt at tilføje længden på beskeden (som skal kunne
> aflæses før beskeden parses). Desuden sender flash altid \0 som
termintator
> på beskeder. Hvis der kommer en besked fra klienten modtager man bare
aldrig
> terminatoren - eller, den kommer ikke med når ByteBuffer'en aflæses.

For at det skal virke, skal klienten sørge for, at flushe efter hver afsendt
streng (med mindre der straks efter sendes en ny XML-streng), og serveren
skal blive ved med at læse og opsamle data indtil den har modtaget det
afsluttende \0. Bemærk, at dette \0 sagtens kan befinde sig midt i en blok
af data hvis klienten når at sende starten på næste streng, og serveren bør
derfor sørge for at håndtere dette fx. ved at flytte det efter \0 ind i en
buffer der bruge til at starte næste læsning med. Hvis klienten ikke flusher
bør de underliggende protokollag (XMLSocket implementeringen eller TCP/IP)
dog alligevel sende uafsendt data efter et vist stykke tid.


> Jeg fandt en måde at simulere problemet på, hvilket selvfølgelig gør det
> nemmere at finde problemet, nemlig ved på min testklient ("normal"
> stream-klient) at flushe output-streamen efter hvert enkelt sendt tegn; i
> stedet for efter at hele besked er sendt til output-streamen. Det gør at
> serveren oftest modtager beskeden opslittet.

Ja, en server kan aldrig regne med at få al data på en gang. Det er derfor
server/klient protokollen bliver nødt til at være udformet så serveren kan
regne ud hvornår den er færdig med at modtage. I dit tilfælde skal serveren
alstå blive ved med at opsamle data indtil et \0 er modtage og alt før dette
\0 kan så parses.


> Hvis jeg reagerer på parse-fejlen ved først at cleare bufferen
> (ByteBuffer.clear())og derefter læse en ekstra gang lykkes det mig at
> modtage præcis de manglende data. At dette faktisk er løsningen
forudsætter,
> at mit simulerede problem er det samme der opstår i driften.

Det lyder ikke som en god løsning af lade parseren afgøre om du har modtaget
en hel besked, ikke når du nu har en nul-terminering. Hvis du læser for
meget (alstå et \0 plus start af næste XML-streng) vil den første streng
måske parse ok (hvis parseren ignorerer ekstra tegn i slutning), men næste
streng vil mangle starten og aldrig kunne parses.

Hvis det stadig giver problemer så skriv igen og jeg (eller andre) kan evt.
hjælpe med at skrive serverens læse-løkke.


Mvh,
--
Filip Larsen



Søg
Reklame
Statistik
Spørgsmål : 177558
Tips : 31968
Nyheder : 719565
Indlæg : 6408921
Brugere : 218888

Månedens bedste
Årets bedste
Sidste års bedste