eine server-client architektur besteht in der regel aus 4 klassen
server-instanz -> listen/accept
verbindungs-thread -> bei verbindung eines clienten
client-instanz -> der client halt
data-thread -> der gegenpart zum verbindungs-thread des servers
das allgemeine schema ist folgendes
server-instanz
erstellt den serversocket und wartet dann in einem loop mit accept auf verbindungen
so bald sich ein client verbindet wird die angenommene verbindung in einen neuen thread gepackt und dieser gestartet ... dieser übernimmt die gesamte kommunikation mit dem client ...
für die instanz ist die verbindung jetzt hergestellt und weitergreicht ... fertig ... warten auf die nächste verbindung
verbindungs-thread
übernimmt die gesamte kommunikation mit dem client
er bekommt vom server den client-socket und eventuell andere daten *für broadcasts meist noch eine referenz auf die server-instanz selbst*
erst hier werden vom socket die streams geholt
mit Socket.getInputStream() bekommt man den stream auf dem daten vom client eintreffen
mit Socket.getOutputStream() bekommt man den stream auf dem daten zum client gesendet werden
wie du hier auf den trichter gekommen bist das du endweder NUR lesen oder NUR schreiben kannst weis ich nicht ... ist aber bei TCP/IP definitiv nicht so ...
in der regel läuft auch in diesem ein loop der die ganze zeit auf daten vom client wartet und bei erhalt auswertet ...
auch wird in diesem loop dierekt geantwortet ...
durch eine zusätzliche methode in dieser klasse ist es der server-instanz welche eine referenz auf die einzelnen threads hält möglich auch a-synchron daten zu senden ... *z.B. einen chat ... der server sendet die nachricht an alle sofort ... und muss nicht erst auf ein poll vom client warten nach dem motto : hast du neue daten für mich ?*
auch wird in dieser klasse das abbruch- und close-verhalten festgelegt
das abbruch-verhalten beschreibt die art und weise wie sich diese klasse verhalten soll wenn irgend ein fehler auftritt ... ob mit den daten die verarbeitet werden sollen ... mit der verbindung selbst ... oder oder oder ...
das close-verhalten beschreibt wie die verbindung "normal" abgebaut werden soll ...
in der regel gibt es ein key-word dafür ... z.B. quit oder exit welche ausgewertet und entsprechend mit abbau der verbindunge reagiert wird
client-instanz
ich denke hier ist nicht viel zu sagen ...
man sollte lediglich darauf achten das man alles was über eine I/O-verbindung läuft *egal ob lokal auf HDD oder über netzwerk* in eine extra thread-klasse auslagert und via referenzen eine kommunikation ermöglich
beliebtester fehler : innerhalb des EDT *EventDispatchThread - das teil was auf deine eingaben reagiert* lange aufwändige I/O-operationen ausführen welche zum "einfrieren" der GUI führen ... desshalb : alles was nicht dierekt die GUI selbst verändert in eigene threads auslagern
data-thread
der entsprechende gegenpart zum verbindungs-thread des servers
hier gibt es mehrere ansätze ... je nach dem was du brauchst
das wohl bekannteste ist das 2-thread-model
zur beschreibung
der data-thread ist eine thread-instanz welche von der client-instanz angestoßen wird ...
innerhalb dieses data-threads wird die verbindung hergestellt , die sockets geholt und alle handshakes vorgenommen ...
wenn du jetzt auf daten warten würdest hättest du das problem das du keine a-synchronen daten senden könntest ...
was wird hier also gemacht ?
innerhalb des data-thread wird ein weiterer thread gestartet welcher nur für das empfangen der daten vom jeweiligen gegenüber zuständig ist ...
der data-thread selbst dient lediglich zur überwachung der bindung und zum a-synchronen senden von daten ... das empfangen wird komplette vom untergeordneten receiver-thread durchgeführt
das ist natürlich nur eine variante ... aber eine welche fast immer zum einsatz kommt
es gibt auch noch varianten in denen nur dieser eine data-thread genutzt wird ...
andere wiederum starten bis zu 5 oder 10 child-threads um mehrere verbindungen gleichzeitig zu bedienen ...
du siehst : bei der gestaltung kommt es auf die anforderungen an ....
und das alles kannst du mit EINEM TCP/IP-socket umsetzen ... du musst die verbindung nur offen halten und auf beiden seiten auf dein key-word reagieren um die verbindung richtig abzubauen ...
und natürlich musst du im fehlerfall auch entsprechend mit abbruch-anweisungen reagieren
was passiert wenn zwischen dem senden der daten vom client zum server und der antwort mehr zeit benötigt wird
an sich erstmal nichts außer eine wartezeit
dadurch das der client schön in threads aufgeteilt ist läuft er normal weiter und wartet halt einfach bis eine antwort kommt ...
im server sieht es dagegen etwas komplizierter aus
wenn man den verbindungs-thread des servers als eine 1-thread config auslegt *wie ich beschrieben habe* kann dieser immer nur einen befehl gelichzeitig abarbeiten ... was durch zu lange timeouts zum verwerfen von daten im empfangs-puffer führen kann
abhilfe schaffen hier weitere threads ...
diese sollten jedoch nur dort eingesetzt werden wo man weis das die entsprechende anweisung mehr zeit benötigt ... da natürlich jeder weitere thread den server belastet ...
normalerweise braucht man sowas nur bei großen datenbanken und aufwändigen I/O operationen ... für die "einfache" ausführung "normaler" kommandos vergehen in der regel nur wenige millisekunden was bei einer guten verbindung dazu führt das der client die komplette antwort innerhalb ein bis zwei sekunden hat ... *wobei das schon echt ne ganze menge daten wären*
die wohl einfachste umsetzung für eine server-multiclient umsetzung findet sich auf der seite
Kaffee & Kuchen - Datenbank-Anbindung mit JDBC
anhand eines kleinen chat systems ...
! ACHTUNG ! einiges in diesem tutorial ist veraltet / deprecated ... der compiler weist an den entsprechenden stellen darauf hin !
als grundlage ist es aber gut geeignet ...
hoffe ich konnte dir jetzt irgendwie weiterhelfen