TCP Paketverlust? bzw inhalt Verlust

Status
Nicht offen für weitere Antworten.

outbreaker

Bekanntes Mitglied
Hallo

Ich habe einen TCP Sender und eine Empfänger funktioniert soweit auch ganz gut. Nur wenn ich in Linux den Traffic für TCP begrenze also zb knapp unter die Grenze was mein Sender gerade Sendet dann kommen bei dem Empfänger nur noch leere Pakete an!
Ich schicke im Moment als Inhalt immer eine Zahl die ich nach jedem Paket um 1 erhöhe.

mein Sender sieht so aus:

Code:
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import outbreaker.ExceptionErrorHandler;
import outbreaker.debuggen.LogFile;

public class Client {

	private BufferedOutputStream out;
	private Socket socket;
	private LogFile logFile;
	private byte[] data = new byte[1460];
	private Message message = new Message(1460);
	
	public Client() {
		createSocket();
		disableNaglesAlgorithmus();
		run();
	}
	
	protected void createSocket(){
		try {
			socket = new Socket(getAddress(),51000);
			out = new BufferedOutputStream(socket.getOutputStream());
		} catch (IOException e) {
			System.err.println(e);
		}
		initLogFile();
	}
	
	public void initLogFile(){
		logFile = new LogFile("Protocol Files T","TEST","csv");
	}
	
	public InetAddress getAddress(){
		try {
			return InetAddress.getByName("192.168.100.1");
		} catch (UnknownHostException e) {
			System.err.println(this.getClass().toString() + " " + e.toString());
		}
		return null;
	}
	
	public void disableNaglesAlgorithmus(){
		try {
			socket.setTcpNoDelay(false);
		} catch (SocketException e) {
			System.err.println("TCPClient:\n"+
					"enableNaglesAlgorithmus()\n"
					+ e.toString() + 
					"\nServerPort: \t" + 51000 +
					"\nServerAdresse: \t" + getAddress());			
		}
	}
	
	protected void closeSocket() throws IOException {
		socket.close();
	}
	
	protected void send() throws IOException{
		data = message.getNextMessageCounter(); //gibt den byte Array zurück
		out.write(data, 0, data.length);
		out.flush();
		logFile.writeln(message.toString()+";"); //schreibt das gesendete in eine Datei
		try {
			Thread.sleep(3); 
		} catch (InterruptedException e) {
			ExceptionErrorHandler.showException("TCPClient: "+ e);
		}
	}
	
	public void run(){
		while (true)
		{
			try {
				send();
			} catch (IOException e) {
				e.printStackTrace();
				break;
			}
		}
	}
	
}

Mein Empfänger nimmt sich das empfangene Paket und schreibt das in eine Datei

Empfänger Kurzfassung:
Code:
private BufferedInputStream in;
private ServerSocket sersock;
private Socket sock;
private byte[] data;


protected void createSocket() {
		data = new byte[1460];
		try {
			sersock = new ServerSocket(51000);
			sock = sersock.accept();
			in = new BufferedInputStream(sock.getInputStream());
		}
		catch(SocketException se)
		{
			ExceptionErrorHandler.showException("TCP Server: " + "Socket problem " + se.getMessage());
		}
		catch(Exception e)
		{
			ExceptionErrorHandler.showException("TCP Server: " + "Couldn't start " + e.getMessage());
		}

	}

protected void receive() throws IOException {
	in.read(data);
	getLogFile().writeln(new String(data,0,new String(data).indexOf(0)));
}

Ich sende immer Pakete mit fester Länge von 1460 byte.

Die Log Datei auf dem Empfänger sieht so aus:

  • 1
    2
    3
    4
    5
    6
    0
    0
    0
    0
    0
    10
    11
    12
    13
    ...

Die 0 schreib ich immer automatisch rein wenn der Empfangene String die Länge 0 hat


kann mir einer sagen wo die Pakete hin sind?
TCP müsste sich doch darum kümmern das die Pakete immer ankommen oder muss ich da noch was machen?
Ich dachte TCP hält sich die Pakete intern bis sie bestätigt wurden? Muss ich die noch irgendwo aufbewahren?

danke für die hilfe
 

kleiner_held

Top Contributor
Aehm du befindest dich gedanklich auf der komplett falschen Schicht des OSI Referenzmodells.

Also gehen wir mal durch:
1. Wenn du mit einem SocketServer arbeitest, befindest du dich in der Anwendungsschicht. Das bedeutet, die Transport-Schicht (TCP) liegt gekapselt darunter und du kannst dir sicher sein, dass keine Pakete verloren gehen. Wenn dem so waere, wuerde die Transportschicht die Pakete erneut schicken und du wuerdest es nie mitbekommen.
2. Du kannst nicht davon ausgehen, dass dir der SocketInputStream bei jedem Aufruf der read() Methode die gleiche Anzahl an bytes liest, die du auf client-seite in einem write() Aufruf in den SocketAutputStream geschrieben hast. Das widerspricht fundamental dem Konzept von Input- und OutputStreams
3. Was du mit deinem LogAufruf in Zeile 28 erreichen willst bleibt mir ein Raetsel. Ich wuerde dir definitiv davon abraten, auf diese Art aus einem Byte-Array einen String zu machen

Wenn du im server jeweils im 1460 block die bytes einlesen willst, dann geht das ungefaehr so:

Code:
   int currentRead;
   int blockRead = 0;
   byte[] data = new byte[1460];
   while ((currentRead = in.read(data, blockRead, data.length - blockRead )) != -1)
   {
       blockRead += currentRead;
       if (blockRead == data.length)
       {
            // hier den vollstaendig gefuellten block verwerten
            blockRead = 0;
       }
   }
   // stream abbruch, hier die letzten blockRead bytes aus data verwerten

Um den byte array dann in einen String zu konvertieren nimmt man ein Charset oder man arbeitet gleich mit einem Reader.
 

outbreaker

Bekanntes Mitglied
Also ich war gedanklich schon auf der Anwenderschicht nur hat mich das verhalten von meinem Programm wohl etwas verwirrt

habe das jetzt so gemacht wie du beschrieben hast und siehe da funktioniert ohne Probleme

besten Dank

habe es im übrigen auch mit einem Reader versucht

Code:
String s = new BufferedReader(new InputStreamReader(new ByteArrayInputStream( data ))).readLine();
s = s.substring(0, s.indexOf(0));

nur leider ist dieser Code sehr viel langsamer als

Code:
String s = new String(data,0,new String(data).indexOf(0));
 

kleiner_held

Top Contributor
Musst du denn die bytes in deinem Array in einen String umwandeln, oder war das nur zum loggen?

Um den ersten index im byte-array mit dem wert 0 zu finden, ist new String(data).indexOf(0) ziemlich, naja, grauenhaft. Außerdem wird, wenn der Array kein 0 byte enthält, das Ergebnis -1 und dein Code, so wie er dasteht, wird eine Exception erzeugen. Dann lieber in einer kurzen Schleife drüber iterieren und den index herausfischen.

Wenn du dann den byte[] array (bzw die bytes aus dem array bis zum ersten 0 byte) mit dem String Konstruktor String(byte[] bytes, int offset, int length) in einen String konveriteren willst, musst du beachten, das dazu intern das default CharSet der VM genommen wird. Also die Läge des erzeugten Strings ist nicht unbedingt gleich dem length Parameter den du übergeben hast. In der Regel ist eine explizite Angabe des CharSet-Namens sicherer.
 

outbreaker

Bekanntes Mitglied
ich brauche das zum loggen und zum berechnen.

d.h. ich sende nen Zeitwert im long Format und brauche das auch wieder auf einer long Variablen

Ich schreibe bevor ich den Array sende in alle Felder eine 0.

Wie kann ich das mit dem CharSet anders machen? Nen eigenes CharSet?

Das Problem ist das der Code nicht so langsam sein sollte da ich die Zeit messen muss von den Paketen.
 

kleiner_held

Top Contributor
Wandel den übertragenen wert einfach wieder in einen long um. Den kannst du dann auch ganz normal loggen.
Für einen long Wert reichen übrigens 8 bytes aus:

Code:
  // gekalut aus ObjectOutputStraem und ObjectInputStream

  // schreiben:
  private void write(OutputStream out, long value)
  {
      byte writeBuffer[] = new byte[8];
      writeBuffer[0] = (byte)(v >>> 56);
      writeBuffer[1] = (byte)(v >>> 48);
      writeBuffer[2] = (byte)(v >>> 40);
      writeBuffer[3] = (byte)(v >>> 32);
      writeBuffer[4] = (byte)(v >>> 24);
      writeBuffer[5] = (byte)(v >>> 16);
      writeBuffer[6] = (byte)(v >>>  8);
      writeBuffer[7] = (byte)(v >>>  0);
      out.write(writeBuffer, 0, 8);
  }

  // lesen
  private long read(InputStream in)
  {
       int currentRead;
       int blockRead = 0;
       byte[] readBuffer = new byte[8]; 
       while (blockRead <readBuffer.length && (currentRead = in.read(readBuffer, blockRead, readBuffer.length - blockRead )) != -1 )
       {
           blockRead += currentRead; 
       }
       if (currentRead == -1)
       {
           throw new EOFException();
       }
       return (((long)readBuffer[0] << 56) +
                ((long)(readBuffer[1] & 255) << 48) +
                ((long)(readBuffer[2] & 255) << 40) +
                ((long)(readBuffer[3] & 255) << 32) +
                ((long)(readBuffer[4] & 255) << 24) +
                ((readBuffer[5] & 255) << 16) +
                ((readBuffer[6] & 255) <<  8) +
                ((readBuffer[7] & 255) <<  0));
  }
 

outbreaker

Bekanntes Mitglied
Ich möchte aber ein ganzen Ethernetpaket verschicken deswegen auch die 1460 byte.

werde deinen Weg das umzuwandeln mal ausprobieren und morgen berichten.

Ich möchte große Pakete verschicken da ich Analysen von parallelen Datenströmen, Traffic Shapern udn Queues mache. Deswegen auch das Zeit messen usw.
 

outbreaker

Bekanntes Mitglied
hab aber noch ne Frage zu deinem Code
Was macht das ">>>" beim erstellen des Arrays und macht das "<<" beim return?
 

HoaX

Top Contributor
bitshift-operatoren.

wieso nimmst du nich direkt iperf?

oder ping? da kann man auch die paketgröße angeben und die zeit wird auch gestoppt ...
 

kleiner_held

Top Contributor
Mit einem Socket Server funktioniert dein Vorhaben nicht.
Wie ich schon in meinem ersten Post schrieb, versteckt die Anwendungsschicht die Transportschicht vor dir. Auch wenn du einen byte[1460] in den OutputStream schreibst, kannst Du nie sicher sein, das der nur in ein TCP Paket verpackt wird.
Zu möglichen Lösungen siehe den Post von HoaX.
 

outbreaker

Bekanntes Mitglied
Ich benötige verschiedene Datenströme also UDP/TCP um zu Analysieren wie arbeiten die auf einer Leitung parallel was für Möglichkeiten, kann man ergreifen damit der TCP Stream nicht vom UDP heruntergeregelt wird. Damit meine ich das auf einer belasteten Leitung immer TCP nachgibt (aufgrund der Überlastmechanismen usw).

Ping misst die Zeit über einen Rückkanal den möchte ich nicht weiter benutzen.
Ich messe nur den Zeitunterschied den die Pakete bei belastet zum unbelastet Netz haben ohne Rückkanal.

das iperf muss ich mir mal ansehen was das macht das sagt mir auf Anhieb nichts
 

kleiner_held

Top Contributor
Ich glaube nicht, da ist Java durch seine plattformunabhängigkeit eigentlich zu abstrakt. Also ich wüsste jetzt spontan nicht wo ich anfangen sollte zu suchen.
Aber eventuell gibts für dafür schon ein Java Framework, eine Recherche auf SourceForge oder mit Google kann nicht schaden.

Edit: Ansonsten kannst du ja einfach mal eine wenig rumprobieren. Mit der 8-byte Variante und einem flush() am OutputStream dannach sollten die 8 byte auch wirklich in exakt ein TCP-Segment wandern. 1460 ist zu groß, wenn man DSL hat, ist die MSS nur 1452 byte groß, dass wäre dann definitiv nicht ein Segment (habs grad auf Wikipedia nachgelesen) Du muesstest nebenbei einen TCP Sniffer laufen lassen, dann kann man das Verhalten wenigstens fuer einzelne VM's vorhersagen.
 

outbreaker

Bekanntes Mitglied
Danke na ich werde mal suchen

Ich schicke das Paket über ein normales LAN-Netzwerk da sollte es mit 1460 gehen oder nicht?
 

HoaX

Top Contributor
in einen "normalen" ethernet ist die ipv4-mss 1460, ja. wobei selbst das nicht zutreffen muss, da mittlerweile viele nics jumbopakete unterstützen.

hast du mittlerweile das routing hinbekommen dass sich der recher selbst pakete über die leitung schickt? oder wie tust du die zeit 100% synchronisieren?

btw solltest du versuchen deine sätze verständlich zu formulieren ...
outbreaker hat gesagt.:
Ich benötige verschiedene Datenströme also UDP/TCP um zu Analysieren wie arbeiten die auf einer Leitung parallel was für Möglichkeiten, kann man ergreifen damit der TCP Stream nicht vom UDP heruntergeregelt wird.
 

outbreaker

Bekanntes Mitglied
Nein ich habe das Routing nicht hinbekommen :cry:

100%ig Synchronisiert bekomme ich die Rechner auch nicht.

Ich wollte es jetzt so machen das ich erst Pakete ohne Last von einem auf den anderen Rechner schicken und dann die Diverenz aus der zeit im Paket und der auf dem Empfangsrechner berechne. Davon nehme ich den Durchschnitt.
Sende dann wieder und zwar unter Last und kann auch hier die Diverenznehmen. und nun kann ich zwischen den Diverenzen den Laufzeit Unterschied feststellen.

Leider bekomme ich so nicht die Absolute Laufzeit raus :cry:


Falls noch Ideen für das Routing auf einem Rechner vorhanden sind dann bin dafür natürlich offen. Weil das wäre die beste Lösung.


Was ich vor habe:
Zwei Rechner zwischen diesen werden über verschiedene Protokolle Daten ausgetauscht. (über UDP und TCP)
Nun möchte ich die Laufzeit und Datenratenvorteile von Traffic Shapern und Queues ermitteln. Zeiten Messen diese Vergleichen usw.
Beim TCP gibt es verschiedenste Regelungsmechanismen für Überlast und Flusskontrolle. Diese haben zur Folge das TCP seine Datenrate Reduziert immer wenn ein Paket verloren gegangen ist. Pakete gehen auch verloren wenn UDP die Leitung "blockiert" d.h. für sich alleine nutzt. Somit würden kaum noch TCP Daten über die Leitung gehen. Dem kann man mit Traffic Shaper und Queues entgegen wirken. :meld:

hoffe das ist ewas verständlicher ausgedrückt ;-)
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben