# Frage zum Verhalten von SocketChannel.write(.)



## kaesebrot (20. Jul 2008)

Hi, 

ich bin gerade dabei einen nonblocking-Server zu implementieren und setze gerade das Versenden von Nachrichten um.  
Die Daten die ich versenden möchte stelle ich in einem ByteBuffer bereit und übergebe diesen dann an den SocketChannel mittels der write-Methode. 
Da der SocketChannel nonblocking ist kann es laut Doku sein, daß nicht der gesamte Buffer versendet (bzw. in den Socketbuffer kopiert) wird bevor der Methodenaufruf abgeschlossen wird. Dies ist im Speziellen auch der Fall, wenn der Channel bereit für's schreiben ist. 

Meine Fragen hierzu: 

1. Gehe ich richtig in der Annahme, daß die restlichen Daten auch noch versendet werden sobald der Channel dafür bereit ist
2. Ist weiterhin richtig, daß auf den gleichen Puffer zugegriffen wird

Ich hoffe der 1. Punkt stimmt  Falls der 2. Punkt auch stimmen sollte bedeutet das, daß ich für jeden Versand einen neuen Puffer reservieren muß, da ich ja nicht sicher sein kann, ob der alte Inhalt noch benötigt wird. 

Gibt es irgendwo eine Art Referenz-Implementierung für einen nonblocking Server. Ich habe bisher nur relativ primitive Beispiele finden können. 


viele Grüße, 
  Käse


----------



## tuxedo (21. Jul 2008)

Nein, falsche Annahme. Der write() aufruf terminiert. Du kannst dann anschließend mit buffer.remaining() schauen wieviel noch gesendet werden muss.

Hab das bei mit in einer while-schleife gelöst:


```
while (buf.remaining()>0) {
socketChannel.write(buf);
}
```

Damit geht's. Allerdings stellt sich mir die Frage: Gibt es einen Fall in dem das das System blockieren kann, weil gar nie was gesendet werden kann? 

Laut Doku tritt das "nicht senden können" auf, wenn der TX-BUffer auf Netzwerkebene "voll" ist und nicht noch mehr reingesteckt werden kann. Man muss da dann einfach "warten" bis wieder Platz ist und es nchmal versuchen.

- Alex


----------



## kaesebrot (21. Jul 2008)

Hi, 

ich habe mir den Abschnitt in der Doku nochmal genau durchgelesen und du hast recht. Das Verhalten ist sinnig so. Ich werde aber warscheinlich nicht die Lösung mit der while-Schleife nehmen sondern den verbleibenden Buffer schreiben, sobald ich wieder signalisiert bekomme, daß der channel beschreibbar ist. 

danke für deine Hilfe. 


viele Grüße, 
  Käse


----------



## tuxedo (22. Jul 2008)

woher bekommst du dieses "Signal" ?

- Alex


----------



## kaesebrot (22. Jul 2008)

Hi, 

das "Signal" bekomme ich mittels eines Selectors. 

ich habe es gestern abend wie oben angedeutet erfolgreich implementieren können. 



viele Grüße, 
  Käse


----------



## tuxedo (22. Jul 2008)

Der Selector sagt dir doch aber nicht ob im Puffer auf Netzwerk/OS Ebene wieder Platz ist?

Wenn du "schreiben" willst, setzt du den entsprechenden "op interest" auf dem entsprechenden Key. 
Dann kommt das Signal zum schreiben auf dem SocketChannel. Und wenn es dann beim schreiben nicht schnell genug geht (netzwerk sendepuffer voll), dann hast du buf.remaining()>0.

Wenn du jetzt auf einen weiteren Schreibbefehl erst wartest, dann verzögert sich das senden unnötig. Weil i.d.R. dauerts bis zum nächsten Schreibvorgang länger, als bis der Sendepuffer geleert wird. D.h. mit einer while() schleife bist du da i.d.R. besser dran.

- Alex


----------



## kaesebrot (22. Jul 2008)

Hi, 

du hast recht, evtl. könnte ich die Daten für einen Client schneller verschicken, jedoch geht das dann auf Kosten der anderen Clients. Die Zeit die ich in der While-Schleife warte würde, wird somit verwendet um die anderen Clients abzuarbeiten. 

Hast du weitergehende Informationen zu diesem Thema? 


viele Grüße, 
  Käse


----------



## tuxedo (23. Jul 2008)

Nein, hab leider keine weiteren.

Zur "Wartezeit":

Ja, in der Tat muss auch bei der While-Schleife gewartet werden. Aber ich denke man kann davon ausgehen, dass die wartezeit bis wieder Platz im Ausgangspuffer bedeutend kleiner ist, als die Zeit die vergeht, bis ein neuer "write" interest auf dem entsprechenden key gesetzt ist. Und "bedeutend" heisst bei mir > Faktor 2. Gemessen hab ich's noch nicht. Ist auch etwas schwer zu messen. Denn es kommt bei mir, selbst wenn ich meinen Server mit >1000 Clients "stresse", die alle so schnell und so viel wie Möglich Daten an den Server schicken, verdammt selten vor. 

Noch was hierzu:0
>> Die Zeit die ich in der While-Schleife warte würde, wird somit verwendet um die anderen Clients abzuarbeiten. 

In unterschiedlichen Quellen im Netz findet man an vielen Stellen den Hinweis/Tipp, dass man nur mit einem einzigen Thread "senden" soll. Alles andere führt zu Synchronisationsproblemen und damit auch zu Performanceproblemen.
Und wenn man sich an den Ratschlag hält und nur in einem Thread sendet, dann hat man auf Client-Seite nach wie vor das "Problem", dass wenn der Sendepuffer voll ist, ich warten muss bis wieder Platz ist. Da hilft es dann auch nicht "keine while schleife" zu benutzen und mit dem nächsten Paket fortzufahren.
Höchstens auf Serverseite _könnte_ es _eventuell_ was bringen. 

Werde versuchen das mal zu messen...

Gruß
Alex


----------



## Guest (23. Jul 2008)

Hi, 



			
				alex0801 hat gesagt.:
			
		

> In unterschiedlichen Quellen im Netz findet man an vielen Stellen den Hinweis/Tipp, dass man nur mit einem einzigen Thread "senden" soll. Alles andere führt zu Synchronisationsproblemen und damit auch zu Performanceproblemen.


D.h. wenn ich zig Clients auf einem Server habe und eine Nachricht an alle verschicken will, bin ich gezwungen Client für Client abzuarbeiten? Kannst du mir evtl. einen Link geben, wo darauf hingewiesen wird? (Ansonsten suche ich auch heute abend allein danach, hab nur im mom. keine Zeit)



			
				alex0801 hat gesagt.:
			
		

> Und wenn man sich an den Ratschlag hält und nur in einem Thread sendet, dann hat man auf Client-Seite nach wie vor das "Problem", dass wenn der Sendepuffer voll ist, ich warten muss bis wieder Platz ist. Da hilft es dann auch nicht "keine while schleife" zu benutzen und mit dem nächsten Paket fortzufahren.
> Höchstens auf Serverseite _könnte_ es _eventuell_ was bringen.


Hmm.. mehrere Clients gibts es doch nur auf der Serverseite, oder? (Die Clientseite ist mir aktuell nicht wichtig, da es sich um Flash-Clients handelt). 
Ich gehe davon aus, daß jede Verbindung ihren eigenen Buffer hat. Ist das richtig so?



			
				alex0801 hat gesagt.:
			
		

> Werde versuchen das mal zu messen...


Wie geht man da am besten vor? Viele Verbindung von einem ClientPC bringen warscheinlich nicht das gewünschte Ergebnis, oder? 


viele Grüße, 
  Käse


----------



## tuxedo (23. Jul 2008)

>> D.h. wenn ich zig Clients auf einem Server habe und eine Nachricht an alle verschicken will, bin ich gezwungen Client für Client abzuarbeiten? Kannst du mir evtl. einen Link geben, wo darauf hingewiesen wird? (Ansonsten suche ich auch heute abend allein danach, hab nur im mom. keine Zeit) 

Korrekt. So wurde es in mehreren Quellen geschrieben. Aber es steht auch dabei, dass das Geschwindigkeitstechnisch absolut kein Problem sei.

"key"'s über mehrere Threads zu verteilen ist ein gewaltiger Aufwand was die synchronisation angeht (kann da ein Lied davon singen, und ich habe lediglich einen Dispatch-Thread der sich auch ums senden kümmert und mehrere Empfangsthreads die sich ums empfangen kümmern (scheint mir die beste Lösung zu sein)).

>> Ich gehe davon aus, daß jede Verbindung ihren eigenen Buffer hat. Ist das richtig so? 

Korrekt. Es wird zwar gemiltiplext, aber dennoch hat jeder am Server registrierte Client eine eigene Socketverbindung mit einem eigenen Puffer. 

>> Wie geht man da am besten vor? Viele Verbindung von einem ClientPC bringen warscheinlich nicht das gewünschte Ergebnis, oder? 

Ich hab bis jetzt so getestet: Server läuft ganz normal. Dazu hab ich dann 3-5 Clients gestartet, welche jeweils mit 1000 Threads den Server "stressen". Konkret geht's bei mir um eine Art RMI Ersatz, realisiert mit Java NIO und nur einer Socketverbindung pro Client für den Hin und Rückweg (RMI macht da mehr oder weniger was es will). Dass ich nur 3-5 Clients habe, macht bei mir nix aus, da ich auch nur einen Dispatch-Thread habe der die eintreffenden Pakete in die einzelnen Bearbeitungsthreads verteilt. 

Ergebnis bis jetzt: Mein Server schafft 10.000 Remote-Methodenaufrufe von den Clients. PRO SEKUNDE! Getestet hab ich für den extrem-Test via localhost um Netzwerkprobleme auszuschließen. Und bei 10.000 Methodenaufrufen pro Sekunde geht da schon einiges durch die Leitung. AFAIK hat eine Java Socketverbindung per Default 8k Puffergröße. Und bei meinem Extrem-Test hab ich nur vereinzelt 1-3mal pro Minute ein solches "Sendepuffer ist voll" Problem.

Von daher würde ich sagen: Auf einem normalen Serversystem, mit "normaler" Auslastung gibts da keine Geschwindigkeitsprobleme was das leeren des Puffers betrifft.

Nen Link hab ich gerade nicht parat. Müsste ich erst wieder raussuchen (sollte mal wieder meine Bookmarks aufräumen).

- Alex


----------

