Kennung Eins
2007-10-18, 18:47:01
Ich programmiere seit ein paar Jahren an einem System, welches aus mittlerweile 4 Rechnern besteht, die alle unterschiedliche Aufgaben haben: Davon 2 PDAs, 1x Win2k und 1xVista. Auf allen Rechnern laufen .NET Anwendungen.
Ich vermute schon eine Weile, dass die Art und Weise, mit der ich die Systeme miteinander kommunizieren lasse, nicht gerade perfekt ist - aber bisher war das egal (jetzt nicht mehr). Daher wollte ich hier mal ein bisschen Input holen, ob mein Vorgehen so korrekt ist, oder ob man das anders besser machen könnte.
Die Kurzform:
Ich habe Rechner A, B, C, D. Die Struktur sieht etwa so aus
Client Server Client Server Client
A - B - B - C - D
Auf Rechner B läuft sowohl ein Server-Objekt (Stellt Daten für A bereit) als auch ein Client (ruft Steuerinformationen von C ab, um Daten für A festzulegen).
Meine Clients und Server lasse ich folgendermaßen miteinander kommunizieren: Der Server wartet darauf, dass der Client einen Text-String schickt. Z.B. "Next" für den "nächsten Abruf". Empfängt der Server einen String, sucht er (switch-case) danach, ob er den String kennt. Kennt er ihn, ruft er Methoden auf und sendet deren Ergebnis an den Client zurück.
Zusätzlich dazu habe ich vorn in jedem String 4 Kontrollbytes, die die Länge des Strings angeben - denn ich habe des Öfteren am Client nicht alle Bytes empfangen, die der Server geschickt hat (was eigentlich durch TCP nicht passieren dürfte, oder?). Diese 4 Bytes werden natürlich vor der Auswertung des Strings abgeschnitten.
Server:
private void StartServer()
{
try
{
TcpListener server = new TcpListener(localAddr, port);
server.Start();
Byte[] bytes = new Byte[256];
while(runningServer) {
Thread.Sleep(1); if (server.Pending())
{
TcpClient client = server.AcceptTcpClient();
String data = null;
NetworkStream stream = client.GetStream();
int i;
while(((i = stream.Read(bytes, 0, bytes.Length))!=0) && (runningServer) && (startSending))
{
byte[] lengthByte = new byte[4];
if (i >= 4) // bevor ich etwas umwandle kann ich auch gleich überprüfen, ob überhaupt mindestens 4 byte angekommen sind (falls nicht: Übertragungsfehler)
{
for (int i1 = 0;i1<lengthByte.Length;i1++)
lengthByte[i1] = bytes[i1];
}
uint u = BitConverter.ToUInt32(lengthByte,0);
if ((i != u) || (i < 4)) // Datenverlust --> nochmal neu lesen
{
Thread.Sleep(100); // 100ms Wartepause um nicht 10Billionen rekursive Aufrufe zu generieren
data = "Unknown"; // dann nochmal abfragen.
}
else
{
System.Text.StringBuilder myCompleteMessage = new System.Text.StringBuilder();
myCompleteMessage.AppendFormat("{0}", System.Text.Encoding.ASCII.GetString(bytes, 0, i));
data = myCompleteMessage.ToString().Substring(4);
}
// hier wird jetzt überprüft, was für Daten empfangen wurden
switch (data)
{
default: respByte = respUnknown;
break; case "Next":
respByte = respTextByte;
break;
}
// jetzt noch unsere 4 Kontrollbytes davor setzen
byte[] respByteWithDatalength = prepareBytes(respByte);
// und abschicken
stream.Write(respByteWithDatalength, 0, respByteWithDatalength.Length);
}
stream.Close();
client.Close(); }
}
server.Stop();
}
catch(Exception e)
{
MessageBox.Show("Exception: "+e.ToString());
}
}
Und jetzt der Code für den Client:
Client:
private String sendData;
private byte[] send(NetworkStream stream)
{
byte[] data = prepareBytes(sendData); // Text in Byte-Array umwandeln und zuweisen
stream.Write(data, 0, data.Length); // Schreibe die Daten in den Datenstrom (Daten versenden)
return data;
}
private string receive(NetworkStream stream)
{
byte[] data = new Byte[255]; // daten-Array leeren
string responseData = String.Empty; // Antwort-String leeren
System.Text.StringBuilder myCompleteMessage = new System.Text.StringBuilder();
int bytes = 0;
// Incoming message may be larger than buffer size.
do
{
bytes = stream.Read(data, 0, data.Length);
myCompleteMessage.AppendFormat(ni,"{0}", System.Text.Encoding.ASCII.GetString(data, 0, bytes));
}
while(stream.DataAvailable);
responseData = myCompleteMessage.ToString(); // Empfangene Daten in String umwandeln
// Jeder String enthält vorn 4 Kontrollbytes, die die Länge des zu empfangenden Strings anzeigen
byte[] lengthByte = new byte[4];
for (int i = 0;i<lengthByte.Length;i++)
lengthByte[i] = data[i];
uint u = BitConverter.ToUInt32(lengthByte,0);
// wenn die Länge, die vorn im String steht, nicht der tatsächlichen Länge entspricht:
if ((bytes != u) || (responseData.Length < 4)) // Datenverlust --> nochmal neu lesen
{
Thread.Sleep(500); // 500ms Wartepause um nicht 10Billionen rekursive Aufrufe zu generieren
send(stream); // als erstes nochmal die Anfrage schicken
responseData = receive(stream); // jetzt erneut lesen
}
if (responseData == null)
return "(connection error)";
// Den empfangenen String abzüglich der ersten 4 Kontrollbytes übernehmen
responseData = responseData.Substring(4);
return responseData;
}
private void Connect()
{
byte[] data = new Byte[256];
try
{
if (this.running) // Flag zur Thread-Kontrolle
{
TcpClient client = new TcpClient(con, port );
NetworkStream stream = client.GetStream();
while (this.running)
{
sendData = "Next";
send(stream); // Schicke einen Text (siehe Methode "send")
responseData = receive(stream); // empfange vom stream (siehe methode "receive")
Thread.Sleep(1);
}
// Close everything.
stream.Close();
stream = null; // to make sure
client.Close();
client = null; // to make sure
}
}
catch (Exception e)
{
MessageBox.Show("Exception: "+e.ToString());
}
}
Jetzt meine Fragen:
- Macht man das so?
- Wie ginge es besser? (schneller / schöner)
Ich vermute schon eine Weile, dass die Art und Weise, mit der ich die Systeme miteinander kommunizieren lasse, nicht gerade perfekt ist - aber bisher war das egal (jetzt nicht mehr). Daher wollte ich hier mal ein bisschen Input holen, ob mein Vorgehen so korrekt ist, oder ob man das anders besser machen könnte.
Die Kurzform:
Ich habe Rechner A, B, C, D. Die Struktur sieht etwa so aus
Client Server Client Server Client
A - B - B - C - D
Auf Rechner B läuft sowohl ein Server-Objekt (Stellt Daten für A bereit) als auch ein Client (ruft Steuerinformationen von C ab, um Daten für A festzulegen).
Meine Clients und Server lasse ich folgendermaßen miteinander kommunizieren: Der Server wartet darauf, dass der Client einen Text-String schickt. Z.B. "Next" für den "nächsten Abruf". Empfängt der Server einen String, sucht er (switch-case) danach, ob er den String kennt. Kennt er ihn, ruft er Methoden auf und sendet deren Ergebnis an den Client zurück.
Zusätzlich dazu habe ich vorn in jedem String 4 Kontrollbytes, die die Länge des Strings angeben - denn ich habe des Öfteren am Client nicht alle Bytes empfangen, die der Server geschickt hat (was eigentlich durch TCP nicht passieren dürfte, oder?). Diese 4 Bytes werden natürlich vor der Auswertung des Strings abgeschnitten.
Server:
private void StartServer()
{
try
{
TcpListener server = new TcpListener(localAddr, port);
server.Start();
Byte[] bytes = new Byte[256];
while(runningServer) {
Thread.Sleep(1); if (server.Pending())
{
TcpClient client = server.AcceptTcpClient();
String data = null;
NetworkStream stream = client.GetStream();
int i;
while(((i = stream.Read(bytes, 0, bytes.Length))!=0) && (runningServer) && (startSending))
{
byte[] lengthByte = new byte[4];
if (i >= 4) // bevor ich etwas umwandle kann ich auch gleich überprüfen, ob überhaupt mindestens 4 byte angekommen sind (falls nicht: Übertragungsfehler)
{
for (int i1 = 0;i1<lengthByte.Length;i1++)
lengthByte[i1] = bytes[i1];
}
uint u = BitConverter.ToUInt32(lengthByte,0);
if ((i != u) || (i < 4)) // Datenverlust --> nochmal neu lesen
{
Thread.Sleep(100); // 100ms Wartepause um nicht 10Billionen rekursive Aufrufe zu generieren
data = "Unknown"; // dann nochmal abfragen.
}
else
{
System.Text.StringBuilder myCompleteMessage = new System.Text.StringBuilder();
myCompleteMessage.AppendFormat("{0}", System.Text.Encoding.ASCII.GetString(bytes, 0, i));
data = myCompleteMessage.ToString().Substring(4);
}
// hier wird jetzt überprüft, was für Daten empfangen wurden
switch (data)
{
default: respByte = respUnknown;
break; case "Next":
respByte = respTextByte;
break;
}
// jetzt noch unsere 4 Kontrollbytes davor setzen
byte[] respByteWithDatalength = prepareBytes(respByte);
// und abschicken
stream.Write(respByteWithDatalength, 0, respByteWithDatalength.Length);
}
stream.Close();
client.Close(); }
}
server.Stop();
}
catch(Exception e)
{
MessageBox.Show("Exception: "+e.ToString());
}
}
Und jetzt der Code für den Client:
Client:
private String sendData;
private byte[] send(NetworkStream stream)
{
byte[] data = prepareBytes(sendData); // Text in Byte-Array umwandeln und zuweisen
stream.Write(data, 0, data.Length); // Schreibe die Daten in den Datenstrom (Daten versenden)
return data;
}
private string receive(NetworkStream stream)
{
byte[] data = new Byte[255]; // daten-Array leeren
string responseData = String.Empty; // Antwort-String leeren
System.Text.StringBuilder myCompleteMessage = new System.Text.StringBuilder();
int bytes = 0;
// Incoming message may be larger than buffer size.
do
{
bytes = stream.Read(data, 0, data.Length);
myCompleteMessage.AppendFormat(ni,"{0}", System.Text.Encoding.ASCII.GetString(data, 0, bytes));
}
while(stream.DataAvailable);
responseData = myCompleteMessage.ToString(); // Empfangene Daten in String umwandeln
// Jeder String enthält vorn 4 Kontrollbytes, die die Länge des zu empfangenden Strings anzeigen
byte[] lengthByte = new byte[4];
for (int i = 0;i<lengthByte.Length;i++)
lengthByte[i] = data[i];
uint u = BitConverter.ToUInt32(lengthByte,0);
// wenn die Länge, die vorn im String steht, nicht der tatsächlichen Länge entspricht:
if ((bytes != u) || (responseData.Length < 4)) // Datenverlust --> nochmal neu lesen
{
Thread.Sleep(500); // 500ms Wartepause um nicht 10Billionen rekursive Aufrufe zu generieren
send(stream); // als erstes nochmal die Anfrage schicken
responseData = receive(stream); // jetzt erneut lesen
}
if (responseData == null)
return "(connection error)";
// Den empfangenen String abzüglich der ersten 4 Kontrollbytes übernehmen
responseData = responseData.Substring(4);
return responseData;
}
private void Connect()
{
byte[] data = new Byte[256];
try
{
if (this.running) // Flag zur Thread-Kontrolle
{
TcpClient client = new TcpClient(con, port );
NetworkStream stream = client.GetStream();
while (this.running)
{
sendData = "Next";
send(stream); // Schicke einen Text (siehe Methode "send")
responseData = receive(stream); // empfange vom stream (siehe methode "receive")
Thread.Sleep(1);
}
// Close everything.
stream.Close();
stream = null; // to make sure
client.Close();
client = null; // to make sure
}
}
catch (Exception e)
{
MessageBox.Show("Exception: "+e.ToString());
}
}
Jetzt meine Fragen:
- Macht man das so?
- Wie ginge es besser? (schneller / schöner)