Nun, zunächst muss man sich erstmal klar machen was ein “Connection reset” überhaupt ist und was er bedeutet.
Einer schnellen Google-Suche zu folge bedeutet ein “Connection reset” speziell in Java das unerwartet das Ende des Streams gelesen wurde (EndOfFile/EndOfStream) ohne das von der Gegenseite vorher ein sauberer Verbindungs-Abbau eingeleitet wurde.
Mit anderen Worten : der Stream wird während des read() geschlossen, in diesem Fall vermutlich vom Server.
Wie komme ich darauf ? Nun, dein Code ist mal wieder ein gutes Beispiel für semantische Fehler, also Fehler die Auftreten weil der Code vom logischen her falsch ist.
Sehen wir uns deinen Code doch noch mal genauer an :
Du übergibst der Methode einen Socket (alleine hier schrillen schon alle Alarm-Glocken), baust jedes mal wieder von neuem einen BufferedReader um einen InputStreamReader rum (hier kann es zu Datenverlust kommen !), liest dann alles was DIESER BufferedReader noch vom Stream kratzen konnte und gibst das Ergebnis weiter. Anschließend überlässt du die Stream-Objekte ihrer selbst und damit dem GC.
Seit Java7 mit der Einführung von AutoCloseable verhalten sich alle Stream-Klassen so das sie sich nach dem Ende des Blocks automatisch schließen, was auch try-with-resources erst möglich macht.
Was passiert jetzt genau ? Nun, das ist nach der Erklärung relativ simpel : die immer wieder neu erzeugten BufferedReader werden nach dem verlassen des Blocks nicht mehr erreichbar und wandern damit in den GC-pool. Irgendwann läuft dann der GC los, räumt die Streams auf und callt dabei close(). Jetzt ist es aber so das in Java ein Stream in der Regel so implementiert wird das ein close() den eigenen Cache bereinigt und letztendes close() des gekapselten Streams aufruft.
Ergo : wenn der GC durchläuft und dabei tote Streams aufräumt wird einmal die close()-Kette durchlaufen was dazu führt das du von deiner Seite aus einen Verbindungsabbau auslöst der dann vom Server korrekt mit dem terminieren der Streams beantwortet wird.
Warum passiert das so sporadisch : liegt in der Natur des GC. Er läuft halt durch wenn er es für richtig hält. Auch ein System.gc() ändert daran nur wenig, denn damit kann man den GC höchstens bitten : “hier, ich hab Arbeit für dich, könntest du bitte mal …”.
Was wäre nun eine Lösung ?
Nun, eine Lösung wäre das du irgendwo im Konstruktor der Klasse oder eben in der Methode die die Verbindung aufbaut EINMAL den Stream vom Socket holst und auch nur EINMAL einen BufferedReader drumbaust, und dieses EINE Objekt dann wiederverwendest.
Das sieht dann z.B. so aus :
import java.net.*;
class ClientRunnable implements Runnable
{
private Server server=null;
private Socket socket=null;
private BufferedReader in=null;
private PrintStream out=null;
protected ClientRunnable(Server server, Socket socket)
{
this.server=server;
this.socket=socket;
}
public void run()
{
try
{
in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
out=new PrintStream(socket.getOutputStream());
}
catch(Exception e)
{
System.out.println("cant get client-streams");
e.printStackTrace();
return;
}
String line="";
while(true)
{
try
{
line=in.readLine();
if(line==null)
{
break;
}
if(line.equals(""))
{
continue;
}
if(line.equals("DC"))
{
System.out.println("DC DETECTED");
break;
}
server.broadcast(line);
}
catch(Exception e)
{
System.out.println("failed to read message");
e.printStackTrace();
}
}
try
{
in.close();
out.close();
socket.close();
}
catch(Exception e)
{
System.out.println("cant close streams/socket");
e.printStackTrace();
}
server.disconnect(this);
}
protected void send(String msg)
{
try
{
out.println(msg);
out.flush();
}
catch(Exception e)
{
System.out.println("cant send message");
e.printStackTrace();
}
}
}```
Du solltest auf jeden Fall einen konstanten loop haben in dem du read() callst und nicht aus diesem mit einem do-while und ready() raus und immer wieder von vorne da dabei wie gesagt Datenverlust droht.
Das kann man auf zwei Arten umsetzen :
1.) Protokoll : die Nachricht erhält als erste Information wie lang sie denn ist, dann weis man wie viel man lesen muss
2.) feste Buffer-Größe : man sucht sich irgendeinen recht großen Wert (so 8192) und prüft halt bei jedem loop ob diese voll gelesen wurde, dann ist von auszugehen das noch mehr kommt, oder halt ob weniger gelesen wurde und damit die Nachricht komplett ist
Die 2. Möglichkeit würde dann so in die Richtung gehen :
```private byte[] crypt(byte[] data, Cipher cipher) throws Exception
{
ByteArrayInputStream bais=new ByteArrayInputStream(data);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int blockSize=cipher.getBlockSize();
int outputSize=cipher.getOutputSize(blockSize);
byte[] input=new byte[blockSize];
byte[] output=new byte[outputSize];
int inLength=0;
boolean finished=false;
while(!finished)
{
inLength=bais.read(input);
if(inLength==blockSize)
{
int outLength=cipher.update(input, 0, blockSize, output);
baos.write(output, 0, outLength);
}
else
{
finished=true;
}
}
if(inLength>0)
{
output=cipher.doFinal(input, 0, inLength);
}
else
{
output=cipher.doFinal();
}
baos.write(output);
return baos.toByteArray();
}```
Auf die Aussage das der Server laut DOC von seiner Seite die Verbindung nicht beenden würde kannst du dich nicht drauf verlassen, aber wenn du schon von deiner Seite semantische Fehler bei der Verwendung einer Socket-Verbindung machst musst du dich über eine korrekte Antwort des Servers auf das Fehlerverhalten deines Codes nicht wundern.
Versuche mal die entsprechenden Punkte auszubessern und dann noch mal erneut zu testen. Sollte das Problem weiterhin auftreten ist zu vermuten das noch irgendwas im Server nicht stimmt und dieser dann tatsächlich von sich aus die Verbindung trennt.