C#: XAMPP ersetzen



  • Hi,

    gleich vorweg, hab von Webservern leider nicht viel Ahnung.

    Ich mache gerade eine Software für mein Aquarium. Mit einer Webcam wird kontinuierlich ein Bild des Aquariums geschossen. Mittels Bilderkennung umrahme ich die von der Software gefundenen Tiere. Das verarbeitete Bild speichere ich als JPG in den XAMPP\HTDOCS Ordner. Dort liegt eine HTML Datei welche dieses Bild in eine einfache Website integriert. XAMPP muss natürlich laufen.

    Ich kann also nun von außen, also von irgendwo auf der Welt, mit meiner IP Adresse auf die Website zugreifen und somit sehen, was sich im Aquarium gerade tut.

    Nun meine Frage: Ich möchte mir den Umweg über XAMPP sparen. Ist es irgendwie möglich, dass mein C# Programm als Webserver fungiert und wenn ich von außen auf meine IP Adresse zugreife mein Programm sozusagen als Antwort das verarbeitete Bild zurückschickt? Ist das mit halbwegs vertretbarem Aufwand möglich? Oder ist der Zwischenschritt über XAMPP doch besser?





  • Wenn es ganz trivial sein soll, dann ist das mit etwas Einarbeitungszeit lösbar. In dem Fall sendest du einfach beim Ansteuern der Seite das Bild. Je nach Qualität (und damit größe) des Bilds, kann das aber auch ziemlich ineffizient werden.

    Das Thema ist aber recht interessant und es ist nie verkehrt, wenn man weiß, wie das Web eigentlich funktioniert. In dem Fall mal nach HTTP-Protokoll suchen.

    Wenn ich Zeit habe, dann kann ich vielleicht auch ein Beispiel basteln.



  • Danke.
    Hab mir das mal durchgelesen, klingt interessant!

    Jetzt bin ich auch noch auf folgendes gestoßen:
    http://www.codeproject.com/KB/IP/mywebserver.aspx

    Ich denke mal, wenn ich diese Klasse anpasse, erfüllt sie genau meine Wünsche!
    Es geht mir ja nur darum, dass ich ein Bild direkt aus dem Programm heraus übertrage.
    Also einerseits wird regelmäßig, über einen Timer gesteuert, ein Bild gemacht und verarbeitet, und andererseits kommen dann ab und zu Anfragen an den Webserver, und dieser soll einfach nur das zuletzt verarbeitete Bild weiterleiten.



  • ghfgjh schrieb:

    Wenn es ganz trivial sein soll, dann ist das mit etwas Einarbeitungszeit lösbar. In dem Fall sendest du einfach beim Ansteuern der Seite das Bild. Je nach Qualität (und damit größe) des Bilds, kann das aber auch ziemlich ineffizient werden.

    Das Thema ist aber recht interessant und es ist nie verkehrt, wenn man weiß, wie das Web eigentlich funktioniert. In dem Fall mal nach HTTP-Protokoll suchen.

    Wenn ich Zeit habe, dann kann ich vielleicht auch ein Beispiel basteln.

    Ich denke mal mit dem Beispiel von http://www.codeproject.com/KB/IP/mywebserver.aspx komm ich zurecht, aber wenn du ein Beispiel posten willst sag ich natürlich auch nicht nein 😉

    Wie schon gesagt, mir würde es reichen ein Bild, das programmintern als Bitmap vorliegt, zu übertragen. Die Eingabe von MeineIp:MeinPort in die Browser Adresszeile sollte dann dazu führen, dass mein C# Programm das Bild zurückschickt. Und wenns nicht allzu schwierig ist vielleicht noch ein paar Zeilen Text. Das Bild ist übrigens recht klein, also ich glaube nicht dass man da viel optimieren muss. Außerdem ists ja nur ein Hobbyprojekt.



  • Hey,

    ich habe mal ein kleines Beispiel zusammengebaut. Zunächst die HTML-Datei:

    <html>
    <head>
    <title>Test</title>
    </head>
    <body>
    
    <p>Das Bild:</p><img src="/cam">
    
    </body>
    </html>
    

    Und dann der Quellcode des Programms:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    
    namespace HTTP
    {
        class Program
        {
            static void Main(string[] args)
            {
                TcpListener serverSocket = new TcpListener(IPAddress.Loopback, 81);
    
                serverSocket.Start();
    
                Console.WriteLine("Server online.");
    
                for (; ; )
                {
                    using (TcpClient clientSocket = serverSocket.AcceptTcpClient())
                    {
                        NetworkStream stream = clientSocket.GetStream();
    
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            // Uns interessiert nur die erste Zeile der Anfrage, die wir an den Leerzeichen splitten
                            string[] request = reader.ReadLine().ToLower().Split(new char[] { ' ' });
    
                            if (request[0] == "get")
                            {
                                if (request[1] == "/")
                                {
                                    // Startseite
    
                                    using (FileStream file = new FileStream("D:/a.html", FileMode.Open))
                                    {
                                        byte[] buffer = new byte[file.Length];
                                        file.Read(buffer, 0, buffer.Length);
    
                                        SendHeader(stream, file.Length, "text/html");
                                        SendBytes(stream, buffer);
                                    }
                                }
                                else
                                {
                                    // Bild
    
                                    using (FileStream file = new FileStream("D:/a.jpeg", FileMode.Open))
                                    {
                                        byte[] buffer = new byte[file.Length];
                                        file.Read(buffer, 0, buffer.Length);
    
                                        SendHeader(stream, file.Length, "image/jpeg");
                                        SendBytes(stream, buffer);
                                    }
                                }
                            }
                        }
                    }
                }
            }
    
            static void SendBytes(NetworkStream stream, byte[] bytes)
            {
                stream.Write(bytes, 0, bytes.Length);
            }
    
            static void SendString(NetworkStream stream, string str)
            {
                SendBytes(stream, Encoding.ASCII.GetBytes(str));
            }
    
            static void SendHeader(NetworkStream stream, long len, string type)
            {
                SendString(stream, String.Format("HTTP/1.0 200 OK\r\nContent-Type: {0}\r\nContent-Length: {1}\r\n\r\n", type, len));
            }
        }
    }
    

    Wenn die Dateien im gleichen Verzeichnis liegen, dann kann der Laufwerksbuchstabe in den Zeilen 51 und 38 auch weggelassen werden. Überhaupt kann der Pfad natürlich geändert werden.

    Sollte es sich um ein anderes Dateiformat handeln, dann muss der Typ (genannt MIME) angepasst werden. Diesen kann man durch Google leicht herausfinden. Einfach nach MIME und der Dateiendung suchen und dann ausprobieren, ob es korrekt angezeigt wird.

    Das Programm müsste einwandfrei laufen. Vorausgesetzt die zu übertragenden Dateien sind nicht zu groß und passen komplett in den Speicher. Aber auch das ließe sich noch verbessern. Das ganze hier dient ja nur als Beispiel/Grundgerüst.

    Man kommt in diesem Fall auf die Webseite, indem man http://127.0.0.1:81 mit dem Browser aufruft. Der Port kann aber in Zeile 15 geändert werden. Viel Spaß damit.



  • Ich habe noch etwas verbessert (Zeile 32 und 78):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    
    namespace HTTP
    {
        class Program
        {
            static void Main(string[] args)
            {
                TcpListener serverSocket = new TcpListener(IPAddress.Loopback, 81);
    
                serverSocket.Start();
    
                Console.WriteLine("Server online.");
    
                for (; ; )
                {
                    using (TcpClient clientSocket = serverSocket.AcceptTcpClient())
                    {
                        NetworkStream stream = clientSocket.GetStream();
    
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            // Uns interessiert nur die erste Zeile der Anfrage, die wir an den Leerzeichen splitten
                            string[] request = reader.ReadLine().ToLower().Split(new char[] { ' ' });
    
                            if (request != null && request[0] == "get") // ÄNDERUNG: request auch gültig?
                            {
                                if (request[1] == "/")
                                {
                                    // Startseite
    
                                    using (FileStream file = new FileStream("D:/a.html", FileMode.Open))
                                    {
                                        byte[] buffer = new byte[file.Length];
                                        file.Read(buffer, 0, buffer.Length);
    
                                        SendHeader(stream, file.Length, "text/html");
                                        SendBytes(stream, buffer);
                                    }
                                }
                                else
                                {
                                    // Bild
    
                                    using (FileStream file = new FileStream("D:/a.jpeg", FileMode.Open))
                                    {
                                        byte[] buffer = new byte[file.Length];
                                        file.Read(buffer, 0, buffer.Length);
    
                                        SendHeader(stream, file.Length, "image/jpeg");
                                        SendBytes(stream, buffer);
                                    }
                                }
                            }
                        }
                    }
                }
            }
    
            static void SendBytes(NetworkStream stream, byte[] bytes)
            {
                stream.Write(bytes, 0, bytes.Length);
            }
    
            static void SendString(NetworkStream stream, string str)
            {
                SendBytes(stream, Encoding.ASCII.GetBytes(str));
            }
    
            static void SendHeader(NetworkStream stream, long len, string type)
            {
                SendString(stream, String.Format("HTTP/1.0 200 OK\r\nContent-Type: {0}\r\nContent-Length: {1}\r\nConnection: close\r\n\r\n", type, len)); // ÄNDERUNG: Verbindung kann danach geschlossen werden
            }
        }
    }
    


  • Wow, danke für das Service 😃
    Werde das dann mal testen!

    Neben dem Webserver muss ja auch alle x Sekunden ein Bild gemacht werden, und dieses verarbeitet werden. Im Code deines Webservers ist eine Endlosschleife drinnen.
    Das heißt Webserver und Bildverarbeitung müssen parallel nebeneinander laufen, im selben Programm, was aber wegen der Endlosschleife nicht möglich ist.
    Mit Threads hab ich bis jetzt nichts gemacht, aber ich denke mal für sowas würden sich Threads anbieten, oder? Thread 1 macht Bildverarbeitung, Thread 2 macht den Webserver. Und beide laufen nebeneinander ohne sich zu stören.
    Ist das soweit richtig? Wenn ja dann muss ich mich erstmal zu diesen Threads einlesen.



  • Es gibt natürlich Möglichkeiten das anders zu lösen, aber letztendlich läuft auch dort wieder ein Thread im Hintergrund.

    Das Thema Threading ist zwar meistens nicht schwer zu verstehen, aber man sollte sich bewusst sein, dass man sich da auch schnell Synchronisations-Fehler zwischen den Threads einhandeln kann, was zu schwierig zu findenden Fehlern führt.

    Mein Beispiel ist vermutlich nicht 100% korrekt und könnte hin und wieder abstürzen. Ich hab aber auch schon länger nicht mehr in C# programmiert. Trotzdem sollte es das Prinzip verdeutlichen. 🙂



  • Na letztendlich hab ich das Thema Threading eh schon viel zu lange ignoriert, wird eh Zeit dass ich mich damit mal auseinandersetze.

    Bei Fragen meld ich mich wieder.

    Danke vorerst einmal für deine Hilfe!



  • Ich hab mir das erneut durch den Kopf gehen lassen. Du könntest eventuell doch auf Threads verzichten.

    Der Ablauf des Programms wäre dann immer so, dass eine Verbindung angenommen wird und erst direkt danach das neue Bild von der Webcam geladen wird. Dieses wird dann nach der Bildverarbeitung an den Client gesendet. Er hat damit ein recht aktuelles Bild.

    Mag sein, dass es dann eine kleine Ladeverzögerung durch die Bildverarbeitung, etc. gibt, aber das ist ja kein Problem. Die Webseite könnte sich dann über HTML (Meta-Refresh) selber erneut laden lassen. Dabei müsstest du ausprobieren, wie schnell das Programm hinterher kommt und die Update-Geschwindigkeit entsprechend anpassen.

    Das funktioniert natürlich nur dann, wenn die Bildverarbeitung nicht kontinuierlich laufen muss. In dem Fall würd dann tatsächlich kein Weg an Threads vorbei.



  • Du musst wissen, ich MUSS das Thema Threads jetzt endlich mal lernen, deswegen führt sowieso kein Weg daran vorbei. Bin bald mitm Bachelor fertig und hab keine Ahnung von der praktischen Anwendung von Threads.

    Aber trotzdem danke für deine Mühe 🙂
    Hab derzeit leider grad ein bisschen Stress auf der Uni, aber wenn wieder mehr Zeit ist werde ich natürlich deinen Source Code mal durcharbeiten und dann implementieren!



  • C#_ XAMPP ersetzen schrieb:

    Du musst wissen, ich MUSS das Thema Threads jetzt endlich mal lernen, deswegen führt sowieso kein Weg daran vorbei. Bin bald mitm Bachelor fertig und hab keine Ahnung von der praktischen Anwendung von Threads.

    Gute Idee, Threading ist wichtig. Und für die kommenden async-Erweiterungen in C# 5 kann es nicht schaden die Grundlagen zu kennen:
    http://www.albahari.com/threading



  • µ schrieb:

    C#_ XAMPP ersetzen schrieb:

    Du musst wissen, ich MUSS das Thema Threads jetzt endlich mal lernen, deswegen führt sowieso kein Weg daran vorbei. Bin bald mitm Bachelor fertig und hab keine Ahnung von der praktischen Anwendung von Threads.

    Gute Idee, Threading ist wichtig. Und für die kommenden async-Erweiterungen in C# 5 kann es nicht schaden die Grundlagen zu kennen:
    http://www.albahari.com/threading

    Danke, das Tutorial sieht ganz brauchbar aus, werd mir das dann mal durchlesen und durcharbeiten.

    Ich bräuchte eindeutig mehr Zeit...



  • C#_ XAMPP ersetzen schrieb:

    Danke, das Tutorial sieht ganz brauchbar aus, werd mir das dann mal durchlesen und durcharbeiten.

    Ich bräuchte eindeutig mehr Zeit...

    Probekapitel aus "C# 4.0 in a Nutshell - Joseph Albahari".
    Das C#-Buch überhaupt.
    Wenn ich dieses Monster irgendwann mal von Anfang bis Ende verstanden habe, mache ich eine Flasche Sekt auf.



  • Ich hab mich nochmal ran gesetzt und den von mir gezeigten Code überarbeitet. Das ganze sollte nun anständig laufen. Ich habs mit Firefox und Internet Explorer getestet.

    Die Geschwindigkeit ist eigentlich ganz okay. Der Verbindungsaufbau dauert allerdings einen kleinen Moment.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    
    namespace HTTP
    {
        class Program
        {
            static string HttpRoot = "E:/htdocs/";
            static string HttpServer = "My Server";
    
            static int HttpPort = 81;
    
            static Dictionary<string, string> types = new Dictionary<string, string>();
    
            static void Main(string[] args)
            {
                TcpListener listener = new TcpListener(IPAddress.Any, HttpPort);
    
                listener.Start();
    
                types[".html"] = "text/html";
                types[".htm"] = "text/html";
    
                types[".jpg"] = "image/jpeg";
                types[".jpeg"] = "image/jpeg";
    
                while (true)
                {
                    TcpClient client = null;
    
                    try
                    {
                        Console.Write("Awaiting connection... ");
                        client = listener.AcceptTcpClient();
                        Console.WriteLine("Done!");
    
                        if (client.Connected && client.Available > 0)
                        {
                            Console.Write("Receiving request... ");
    
                            byte[] request = new byte[client.Available];
    
                            client.GetStream().Read(request, 0, request.Length);
    
                            string[] header = System.Text.ASCIIEncoding.ASCII.GetString(request).Split(new char[] { '\n' });
                            Console.Write("[" + header[0].Trim() + "] ");
    
                            Console.WriteLine("Done!");
    
                            if (!client.Connected)
                            {
                                continue;
                            }
    
                            string[] requestElements = header[0].Split(new char[] { ' ' });
    
                            if (requestElements[0].ToLower() == "get")
                            {
                                Console.Write("Sending response... ");
    
                                string file = requestElements[1].Substring(1);
    
                                if (requestElements[1] == "/")
                                {
                                    file = "index.html";
                                }
    
                                Console.Write("[Sending file \"" + file + "\"... ");
    
                                if (File.Exists(HttpRoot + file))
                                {
                                    SendFile(client, "200 OK", HttpRoot + file);
    
                                    Console.Write("Done!] ");
                                }
                                else
                                {
                                    SendFile(client, "404 Not Found", HttpRoot + "404.html");
    
                                    Console.Write("Not Found!] ");
                                }
    
                                Console.WriteLine("Done!");
                            }
    
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Error: " + ex.Message);
                    }
                    finally
                    {
                        Console.Write("Closing connection... ");
    
                        if (client != null)
                        {
                            client.Close();
                        }
    
                        Console.WriteLine("Done!\n");
                    }
                }
            }
    
            static void SendHttpHeader(TcpClient client, string code, string type, long length)
            {
                SendHttpString(client, String.Format("HTTP/1.0 {0}\r\nServer: {1}\r\nPragma: no-cache\r\nContent-Type: {2}\r\nContent-Length: {3}\r\n\r\n", code, HttpServer, type, length));
            }
    
            static void SendHttpString(TcpClient client, string str)
            {
                byte[] response = System.Text.ASCIIEncoding.ASCII.GetBytes(str);
    
                client.GetStream().Write(response, 0, response.Length);
            }
    
            static void SendFile(TcpClient client, string code, string path)
            {
                try
                {
                    using (FileStream fs = new FileStream(path, FileMode.Open))
                    {
                        string type = "text/plain";
    
                        string extension = Path.GetExtension(path);
    
                        if (types.ContainsKey(extension))
                        {
                            type = types[extension];
                        }
    
                        SendHttpHeader(client, code, type, fs.Length);
    
                        byte[] buf = new byte[2048];
    
                        long total = 0;
                        long size = fs.Length;
                        long left = size;
    
                        while (total < size)
                        {
                            int rc = fs.Read(buf, 0, buf.Length);
    
                            client.GetStream().Write(buf, 0, rc);
    
                            total += rc;
                            left -= rc;
                        }
    
                        Console.Write(String.Format("(Sent {0} of {1} bytes: {2}) ", total, size, path));
                    }
                }
                catch
                {
                    string msg = "500 - Internal Server Error";
    
                    SendHttpHeader(client, "500 Internal Server Error", "text/html", msg.Length);
    
                    SendHttpString(client, msg);
                }
            }
        }
    }
    

    Die Beschränkung der Dateigröße ist auch nicht mehr vorhanden. Es können also nun auch größere Dateien versendet werden. Angepasst werden muss der Pfad zum Verzeichnis, wo alle Dateien liegen. Die Startseite muss index.html heißen.


Log in to reply