Laden...

Wie kann ein TCP Server mit mehreren Clients arbeiten?

Erstellt von milchmanno vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.088 Views
M
milchmanno Themenstarter:in
6 Beiträge seit 2019
vor 5 Jahren
Wie kann ein TCP Server mit mehreren Clients arbeiten?

Hi,

ich habe mir hier einen Tcp Server zusammen gebastelt.. bin noch relativ unerfahren in dem Gebiet und wollte fragen, ob (oder besser wie..) es möglich ist, das der Server auch mehrere Clients haben kann. Oder im besten Fall beliebig viele. Über ein Codebeispiel oder sogar eine Verbesserung würde ich mich sehr freuen.

Im Moment empfängt der Server die CPU-Last von einem Client, was dann in dem Label lblReceivedData ausgegeben wird.


namespace Server
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
            this.Size = Properties.Settings.Default.Size;
            this.Location = Properties.Settings.Default.Location;
            this.WindowState = Properties.Settings.Default.WindowState;
        }
        
        private void btnStartServer_Click(object sender, EventArgs e)
        {
            try
            {
                Thread obj_thread = new Thread(StartServer);
                obj_thread.Start();
                MessageBox.Show("Server erfolgreich gestartet", "Erfolgreich", MessageBoxButtons.OK, MessageBoxIcon.Information);
                btnStartServer.Text = "running...";
                btnStartServer.Enabled = false;
            }
            catch (Exception x)
            {
                MessageBox.Show(x.Message, "Error", MessageBoxButtons.OK);
            }
        }

        TcpListener obj_server = new TcpListener(IPAddress.Any, 6868);
        
        public void StartServer()
        {
            obj_server.Start();
            while (true)
            {
                TcpClient tc = obj_server.AcceptTcpClient();
                NetworkStream ns = tc.GetStream();
                if (ns.ReadByte() == 2)
                {
                    byte[] receive_data = ReadStream(ns);
                    lblReceivedData.Invoke((MethodInvoker)(() => lblReceivedData.Text = Encoding.UTF8.GetString(receive_data)));
                }
            }
        }

        public byte[] ReadStream(NetworkStream ns)
        {
            byte[] data_buff = null;
            int b = 0;
            String buff_length = "";
            while ((b = ns.ReadByte()) != 4)
            {
                buff_length += (char)b;
            }
            int data_length = Convert.ToInt32(buff_length);
            data_buff = new byte[data_length];
            int byte_read = 0;
            int byte_offset = 0;
            while (byte_offset < data_length)
            {
                byte_read = ns.Read(data_buff, byte_offset, data_length + byte_offset);
                byte_offset += byte_read;
            }
            return data_buff;
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Properties.Settings.Default.WindowState = this.WindowState;
            Properties.Settings.Default.Size = this.Size;
            Properties.Settings.Default.Location = this.Location;
            Properties.Settings.Default.Save();
        }
    }
}

16.807 Beiträge seit 2008
vor 5 Jahren

Naja; das ist das was ich im anderen Thread gemeint habe: Du musst Dich jetzt um das gesamte Verbindungsmanagement selbst kümmern, was Dir eben ASP.NET Core abnehmen würde.
Das wird jetzt nen riesen Rattenschwanz....

Das heisst Du brauchst jetzt erstmal eine Art Container oder Liste, wo Du jede einkommende Verbindung quasi als offene Session verwalten musst.
Hinzu kommt eben

  • Security (Auth und Co)
  • Prüfen ob Verbindungen überhaupt noch gültig sind
  • Timeouts
  • Identification
  • ...
    Ich würde mir das wirklich mehrmals überlegen, das wirklich so weiter zu treiben.....
    Konzentrier Dich lieber auf den Mehrwert der Anwendung und lass Dir die Infrastruktur durch Frameworks abnehmen: dafür wurden sie erfunden.
    Keine 100 Zeilen Code und Du hast das sauber mit nem REST-Client und ASP.NET Core... oder völlig entkoppelt mit Hangfire (~70 Zeilen).
T
2.219 Beiträge seit 2008
vor 5 Jahren

@Abt
Die Frage ist, ob er dies überhaupt möchte.
Aktuell liest sich das für mich sehr nach einem Lernthema mit dem er sich in Netzwerkprogrammierung einarbeiten will.
Ich halte es daher für den falschen Ansatz ihn davon abbringen zu wollen.
Wenn es zum lernen ist, sollte er hier auch sich damit beschäftigen.
Gerade als Einsteiger sollte man auch TCP/UDP mal programmiert und verstanden haben.

Auf ASP .NET Core würde ich dann im nächsten Schritt eingehen oder wenn es Richtung Web/HTTP geht.
Dann kann man sich ASP .NET Core als fertiges Gesamtpaket anschauen.
Aber aktuell würde ich davon ausgehen, dass dies für den TE nicht das Ziel ist.

@milchmanno
Die TCPListener Klasse bietet hier die AcceptTcpClientAsync Methode.
Diese liefert dir dann einen Task mit dem TcpClient.
Diesen kannst du dann weiterverarbeiten wiederum in einer asynchronen Methode weiterverarbeiten.

Die AccceptTcpClient Methode blockiert bis sich ein Client verbunden hat, weshalb du auch immer nur eine Verbindung annehmen kannst.

Link:
https://docs.microsoft.com/de-de/dotnet/api/system.net.sockets.tcplistener.accepttcpclientasync?view=netframework-4.7.2

Beispiel (Ungetestet):
https://gist.github.com/jamesmanning/2622054

Für Lernzwecke solltest du auch die Doku gründlich lesen.
Den Thread solltest du am besten auch durch einen Task ersetzen, diese lassen sich dann auch besser verarbeiten und beendet.
Ebenfalls wird bei einem eigenen Thread Objekt ein neuer Thread vom OS angefordert, während Tasks sich einen Thread aus dem Threadpool nehmen.
Hier solltest du immer den Threadpool deiner Anwendung verwenden.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

M
milchmanno Themenstarter:in
6 Beiträge seit 2019
vor 5 Jahren

@Apt

Danke für deine Antwort, du hast bestimmt Recht.
Allerdings muss ich leider eine Windows Form programmieren, da ich ein grafisches UI brauche und mich mit z.B. Web API noch nicht oder nur kurz befasst habe, und meine Kenntnisse einfach noch nicht weit genug sind. Ich werde mir aber auf jeden Fall mal den REST-Client und die ASP.Net Core Version anschauen!

@T-Virus

Danke auch für deine Antwort, die Tipps waren sehr hilfreich. Werde sofort loslegen.

5.657 Beiträge seit 2006
vor 5 Jahren

Die Implementierung der Netzwerk-Kommunikation und die Benutzeroberfläche sind aber zwei unterschiedliche Dinge, die auch unabhängig voneinander implementiert werden können und sollten. Siehe dazu [Artikel] Drei-Schichten-Architektur

Weeks of programming can save you hours of planning

16.807 Beiträge seit 2008
vor 5 Jahren

Aktuell liest sich das für mich sehr nach einem Lernthema mit dem er sich in Netzwerkprogrammierung einarbeiten will.

Dann les mal die anderen Themen, die wir hier schon zu dieser Software hatten.
Es ist ein Remote Tool für die Serverüberwachung.

Ich halte es daher für den falschen Ansatz ihn davon abbringen zu wollen.

Ich nicht, denn er bindet sich ein riesen Klotz an den Fuß.

Auf ASP .NET Core würde ich dann im nächsten Schritt eingehen oder wenn es Richtung Web/HTTP geht.

ASP.NET Core ist nicht HTTP-only. Bitte die ASP.NET Core Basics zu Middlewares anschauen.

Die AccceptTcpClient Methode blockiert bis sich ein Client verbunden hat, weshalb du auch immer nur eine Verbindung annehmen kannst.

Das stimmt nicht.
AcceptTcpClient ist nur für den initialen Verbindungsaufbau verantwortlich; beschränkt aber nicht die Menge an Verbindungen, die angenommen werden können.

Ja, die asynchrone Methode macht die Sache insgesamt einfacher; ändert die Grundstruktur hier nicht.
Das Blockierne von AcceptTcpClient ist hier im Endeffekt egal, weil es bereits in einen extra Thread ausgelagert wurde.
Es ist hier auch korrekt, dass dies ein extra Thread ist - ein einfacher Task reicht hier nicht (ausser der Task wird mit Task.Run() gestartet, sodass der einzelne Task dann auch in einem eigenen Thread läuft).

Aber das allein ändert die Sache nicht, dass der Code hier nicht für mehrere Clients ausgelegt ist - und zusätzlich eben die starke Vermischung mit der UI.

3.170 Beiträge seit 2006
vor 5 Jahren

Hallo,

ich stimme Abt zu. Wenn es um eine Produktivsoftware geht, bitte nicht selber auf TcpListener/TcpClient-Ebene anfangen. Bei allen heutigen Möglichkeiten eignet sich das wirklich nur als Lernthema, oder wenn man was in bestehenden Netzwerksystemen einbinden muss und daher keine anderen Möglichkeiten hat.

Dennoch: Die eigentliche Frage wird in [FAQ] TcpClient: einfaches Beispiel umfangreich behandelt.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

1.696 Beiträge seit 2006
vor 5 Jahren

Wenn ich richtig (aus anderen Thread) verstanden habe, möchtest du ein Netzwerk-Monitoring aufbauen? Ich würde keine Client/Server Applikation schreiben, sondern auf SNMP/WMI zurückgreifen und die Daten per Dienst live remote abfragen und darstellen, eine Datenbank als temporäre Ablage sollte das Vorhaben gut unterstützen. Die Anwendung kannst du sogar auf mehreren Rechner als Sensor-Server laufen lassen, welche verschiedene Typen von zu überwachenden Client überwacht und Daten in die DB schreibt. Zur Darstellung der Daten kann man einen Webserver dafür verwenden, welche die Daten aus der DB liest und anzeigt

Nur so als Idee 😃

Ich bin verantwortlich für das, was ich sage, nicht für das, was du verstehst.

**:::