C# "Datei senden"-Programm - Simple switch/case-Abfrage zerstört das ganze Programm?

Jack159

Lieutenant
Registriert
Dez. 2011
Beiträge
766
Hi,

Ich schreibe gerade an einem simplen "Datei senden"-Programm in C#. Dabei habe ich 2 fast identische Versionen.

1. Version:
Funktionert einwandfrei!

2. Version:
Funktioniert nicht einwandfrei, sondern weist einen sehr seltsamen Fehler auf.

Der Unterschied zwischen beiden Versionen ist legidlich, dass ich bei der 2. Version nicht sofort auf der Serverseite die Datei empfange, sondern erst eine Statusanweisung ala "/sendFile", welche vom Client geschickt wird, zunächst abfrage, und dann erst eine entsprechene sendFile() Methode auf dem Server aufrufe, welche die Datei empfängt.
Der Hintergrund ist einfach, dass ich später vom Client auch Daten löschen, runterladen oder eben senden/hochladen können will. Daher diese Statusanweisungen.

Das seltsame ist nun, dass bei der 2. Version die Datei nicht vollständig empfangen wird, sondern der Server mitten im Empfangsprozess plötzlich aufhört. Das Programm hängt sich nicht auf, es gibt keine Exceptions, es hört einfach auf....

Die Stellen, um die es hier geht bzw. in denen der Fehler liegen muss, habe ich mit
Code:
 // ####################################################################
Blöcken umrahmt. Bei der 1. Version des Clients ist dieser leer, das ist schon richtig so.

Ich verstehe nicht im geringsten, wo da der Fehler liegen soll?! O.o
Im Anhang sind die Ausgaben beider Versionen angehangen.

P.S.
Ja ich nutze keine using-Anweisungen, aber darin liegt es nicht. Version 1 funktioniert ja einwandfrei.


1. Version - Server:
Code:
namespace FTP___Server_v2 {
    public class Server {

        private IPAddress ipadress;
        private TcpListener serverconnection;
        private TcpClient clientconnection;
        public NetworkStream nws { get; set; }
        public StreamReader sr { get; set; }
        public StreamWriter sw { get; set; }

        public Server() {
            startServer();
            waitForClient();
        }

        public void startServer() {
            ipadress = IPAddress.Parse("127.0.0.1");
            serverconnection = new TcpListener(ipadress, 5555);
            serverconnection.Start();
        }

        public void waitForClient() {
            Console.WriteLine("Server startet. Waiting for incoming client connections...");
            clientconnection = serverconnection.AcceptTcpClient();
            nws = clientconnection.GetStream();
            sr = new StreamReader(nws);
            sw = new StreamWriter(nws);
            Console.WriteLine("Client connected with Server!");

            receiveMessage();
        }

        public void receiveMessage() {

            // ####################################################################
            sendFile(); 
            // ####################################################################

        }

        public void sendFile() {
            // Recieve filename
            string filename = sr.ReadLine().Remove(0, 10);

            // Recieve filesize
            long filesize = Convert.ToInt64(sr.ReadLine().Remove(0, 10));

            Console.WriteLine("Filename: " + filename);
            Console.WriteLine("Filesize: " + filesize);

            long count = filesize;
            Byte[] fileBytes = new Byte[1024];
            var a = File.OpenWrite(filename);
            while (count > 0) {
                int recieved = nws.Read(fileBytes, 0, fileBytes.Length);
                Console.WriteLine("recieved = " + recieved);
                nws.Flush();
                a.Write(fileBytes, 0, recieved);
                a.Flush();
                count -= recieved;
            }

            a.Dispose();
            Console.WriteLine("File was written on HDD. Finish!");
            Console.ReadLine();
        }
        
    }
}


1. Version - Client:

Code:
namespace FTP___Client_v2 {
    public partial class Form1 : Form {

        public string name { get; set; }
        public TcpClient clientConnection { get; set; }
        public NetworkStream nws { get; set; }
        public StreamReader sr { get; set; }
        public StreamWriter sw { get; set; }

        public int sizeOfConnectedClients { get; set; }
        public string clientnamelistString { get; set; }

        public Form1() {
            InitializeComponent();
            connectToServer();
        }

        private void btnUpload_Click(object sender, EventArgs e) {

            OpenFileDialog ofd = new OpenFileDialog();
            string filename = null;
            string filepath = null;
            if (ofd.ShowDialog() == DialogResult.OK) {
                filename = System.IO.Path.GetFileName(ofd.FileName);
                //tbFilename.Text = System.IO.Path.GetFileName(ofd.FileName);
                filepath = ofd.FileName;
                //tbFilesize.Text = (new FileInfo(ofd.FileName).Length).ToString() + " KB";
            }

            Console.WriteLine("filename: " + filename);
            var a = File.OpenRead(filepath);

            FileInfo fo = new FileInfo(filepath);
            long filesize = fo.Length;
            Console.WriteLine("Filesize: " + filesize);

            // ####################################################################

            // ####################################################################

            // Send filename to server
            sw.WriteLine("Filename: " + filename);
            sw.Flush();

            // Send filesize to server
            sw.WriteLine("Filesize: " + filesize);
            sw.Flush();

            // Write file into fileBytes-Array and sends it in parts
            Byte[] fileBytes = new Byte[1024];
            long count = filesize;
            while (count > 0) {
                int recieved = a.Read(fileBytes, 0, fileBytes.Length);
                a.Flush();
                nws.Write(fileBytes, 0, recieved);
                nws.Flush();
                count -= recieved;
            }

            Console.WriteLine("File sends!");

        }

        public void connectToServer() {

            Console.WriteLine("connect to server...");
            clientConnection = new TcpClient();
            IPAddress ipadress = IPAddress.Parse("127.0.0.1");
            clientConnection.Connect(ipadress, 5555);
            Console.WriteLine("connected to server!");

            nws = clientConnection.GetStream();
            sr = new StreamReader(nws);
            sw = new StreamWriter(nws);

        }
    }
}

Ausgabe:
v1.png





2. Version - Server (Nur was sich geändert hat):
Code:
        public void receiveMessage() {
            // ####################################################################
            string receivedMessage = sr.ReadLine();

            switch (receivedMessage) {
                case "/sendFile": {
                        sendFile();
                        break;
                    }
                default: {
                        Console.WriteLine("Received undefined message: " + receivedMessage);
                        break;
                    }
            }
            // ####################################################################
        }


2. Version - Client (Nur was sich geändert hat):
Code:
private void btnUpload_Click(object sender, EventArgs e) {
            //...
            // ####################################################################
            sw.WriteLine("/sendFile");
            sw.Flush();
            // ####################################################################
            //...
        }
Ausgabe: (Wie man sieht, hört er mitten drinnen plötzlich auf....Warum auch immer)
v2.png
 
Also auf die schnelle kann ich nicht erkennen, was falsch sein soll. Am switch liegt es allerdings sicher nicht. Es sei aber mal der Artikel von DotNetPerls ans Herz gelget: C# If vs. Switch.

Grundsätzlich sei auch noch angemerkt, dass sowohl Streamreader als auch Streamwriter nach Benutzung geschlossen und nicht dauer offen bleiben sollten.
 
PapstRatze schrieb:
Also auf die schnelle kann ich nicht erkennen, was falsch sein soll. Am switch liegt es allerdings sicher nicht.

Das einzige was sich aber doch nur geändert hat, ist ein hinzugekommenes switch. Es kann eigentlich nur daran liegen. Allerdings wüsste ich nicht im geringsten wieso es daran liegen sollte oder was da den Fehler verursacht. Version 1 funktioniert ja einwandfrei....

Was ja seltsam ist, ist, dass anscheinend die Zeile
Code:
int recieved = nws.Read(fileBytes, 0, fileBytes.Length);
blockiert. Also die Read()-Methode des Networkstream-Objekts nws blockiert also. Das komische ist aber, dass laut API diese Methode garnicht blockiert:

Wenn keine Daten zum Lesen zur Verfügung stehen, gibt die Read-Methode 0 (null) zurück. Die Read-Operation liest alle verfügbaren Daten bis zu der im size-Parameter angegebenen Anzahl von Bytes. Wenn der Remotehost die Verbindung schließt und alle verfügbaren Daten empfangen wurden, wird die Read-Methode sofort abgeschlossen, und diese gibt 0 (null) Bytes zurück.

Die Streams habe ich mal am Ende geschlossen, aber das behebt das Problem nicht.
 
Zuletzt bearbeitet:
Geh doch einfach mal mit dem Debugger durch?
 
e-Laurin schrieb:
Überprüfe mal, ob die gesendete Dateigröße richtig ist. Eventuell funktioniert die Konvertierung nicht richtig oder es wird falsch gesendet/übertragen.

Bei der 2. Version (der nicht funktionierenden Version) stimmt die Dateigröße der Datei, die vom Server empfangen/gespeichert wird, nicht. Sie ist kleiner als die soll-Größe. Das liegt aber daran, dass in der while-Schleife auf der Serverseite, in der die Datei empfangen und gespeichert wird, die Serveranwendung "stehen bleibt" bzw. die read() Methode anscheinend blockiert, was sie laut API aber überhaupt nicht tut.

Die 1. Version funktioniert ja einwandfrei. Das einzig neue in der 2. Version ist der switch/case Block. Aufgrunddessen tritt das Problem auf. Warum auch immer...

Ich habe ja schon oft Fehler gehabt, aber hier habe ich jetzt wirklich fast nichts geändert. Ich verstehe nicht, wieso es plötzlich nicht mehr geht...



@ Darlis:
Wozu? Ich weiß ja, dass die read() Methode blockiert, warum auch immer.
 
Ich habe mir den Code mal etwas genauer angeschaut:
1. Du verwendest Flush() bei lesenden Zugriff. Was erhoffst du dir davon?
2. Du hast vermutlich das Konzept der TCP-Datenübertragung nicht verstanden:
Code:
            // Send filename to server
            sw.WriteLine("Filename: " + filename);
            sw.Flush();
Das bedeutet nicht, dass auf der Gegenseite zwingend "Filename: ..." empfangen wird, wenn dort ein Read ausgeführt wird. Das können mehr oder weniger Informationen sein. Das erste Read liefert z.B "Filenam", das 2. dann "e: ...". Da du auf der Gegenseite ReadLine verwendest, hast du das Problem (unbewusst?) umgangen.
3. Der Methodenname sendFile ist irreführend. Du sendest dort keine Datei.
4. In der while-Schleife gibt es noch mehr Methoden außer nws.Read. Woher bist du dir so sicher, dass genau diese Methode blockiert, die laut Doku gar nicht blockieren soll?

Daher: Geh mit dem Debugger durch, wechsel mehrmals zw. Client und Server oder lasse nur den Client und dann nur den Server arbeiten und schau' dir den Inhalt der Variablen an.
 
Ich habe jetzt den Debugger durchlaufen lassen, aber mir ist nichts aufgefallen, was nicht bereits in der Ausgabe steht.
Es blockiert definitiv die nws.read() Methode, da ich es eben beim debuggen gesehen habe.
nws.flush() auf der Serverseite habe ich rausgenommen, brachte aber nichts.





Neue Erkenntnis:

Jetzt wird es richtig seltsam!:

Ich habe auf der Serverseite die Methode "sendFile()" rausgenommen und den Inhalt der "sendFile()" Methode direkt in die "receiveMessage()" Methode reingepackt:

Code:
        public void receiveMessage() {

            // hier wird dann "/sendFile" empfangen....Aus dem ursprünglichen switch/case Block
            string receivedMessage = sr.ReadLine();

            // Recieve filename
            string filename = sr.ReadLine().Remove(0, 10);

            // Recieve filesize
            long filesize = Convert.ToInt64(sr.ReadLine().Remove(0, 10));

            Console.WriteLine("Filename: " + filename);
            Console.WriteLine("Filesize: " + filesize);

            long count = filesize;
            Byte[] fileBytes = new Byte[1024];
            var a = File.OpenWrite(filename);
            while (count > 0) {

                int recieved = nws.Read(fileBytes, 0, fileBytes.Length);
                Console.WriteLine("recieved = " + recieved);
                //nws.Flush();
                a.Write(fileBytes, 0, recieved);
                a.Flush();
                count -= recieved;
            }

            a.Dispose();
            Console.WriteLine("File was written on HDD. Finish!");
            Console.ReadLine();
}


So funktioniert es plötzlich...

Aber wieso? Wieso geht es nicht mehr, sobald das ganze in eine seperate Methode gepackt wird? O.o
 
Zuletzt bearbeitet:
Darlis schrieb:
Die Doku zur Read-Methode scheint falsch zu sein: http://stackoverflow.com/questions/6958255/what-are-some-reasons-networkstream-read-would-hang-block
Der Trick ist auf DataAvailable zu prüfen, wenn nicht blockiert werden soll.

Ich habe es mal probiert (in die ursprüngliche Version 2 eingebaut, wo noch die sendFile() existiert):

Code:
            while (count > 0) {
                if (nws.DataAvailable) { 
                    int recieved = nws.Read(fileBytes, 0, fileBytes.Length);
                    Console.WriteLine("recieved = " + recieved);
                    a.Write(fileBytes, 0, recieved);
                    a.Flush();
                    count -= recieved;
                }
            }

Selbes Problem wie gehabt. Es erfolt genau die gleiche Ausgabe. Das Problem ist also, dass der Server plötzlich nichts mehr empfängt. Aber warum?

Und warum geht es plötzlich, wenn ich das tue, was ich in meinem voherigen Post beschrieben habe?
Und warum geht Version 1?

Da ist doch nichts anderes?! Ich probiere es ja immer mit der selben Datei (Bei anderen Dateien tritt der Fehler natürlich auch auf). Es wird exakt jedesmal die selbe Anzahl an Bytes gesendet. Da ist nichts anders.

Alternativ geht natürlich auch:

Code:
            while (nws.DataAvailable) {
                    int recieved = nws.Read(fileBytes, 0, fileBytes.Length);
                    Console.WriteLine("recieved = " + recieved);
                    a.Write(fileBytes, 0, recieved);
                    a.Flush();
                    count -= recieved;  
            }

Dann läuft das Programm nun zwar bis zum Ende durch, aber es ändert nichts daran, dass die Datei unvollständig empfangen wird, weil er ja plötzlich mittendrinnen nichts mehr empfängt und abbricht.
 
Es gibt nur eine Möglichkeit, dass Read blockiert: Es wird nichts mehr gesendet.
Also guck mal, ob der Client auch alles schreibt, was du am Server lesen willst.
Auch noch ne Idee: In die Datei ein sinnvollen Text (Lorem ipsum o.Ä.) und den auf der Konsole ausgeben lassen, dann siehst du, ob du die Header richtig verarbeitest.
 
Falls es noch wen intressiert, ich habe das Problem gefunden. Das Problem war, dass ich sowohl über den Networkstream als auch über den Streamreader/Streamwriter gelesen/geschrieben habe. Das Problem ist, dass diese beiden Streams ja miteinander verkettet wurden.
Dadurch kam es zu Problemen.

Die Lösung ist, dass man die Datei-Bytes nicht über den Networkstream schreibt/liest, sondern über den Streamreader/Streamwriter, über den auch die ganz normalen Strings hier verschickt werden.


Dennoch danke an alle die mitgeholfen haben!
 
Zurück
Oben