Du bist nicht angemeldet.

kevin89

Super Moderator

  • »kevin89« ist männlich
  • »kevin89« ist der Autor dieses Themas

Beiträge: 3 881

Dabei seit: 16. April 2008

Hilfreich-Bewertungen: 155

  • Private Nachricht senden

1

Dienstag, 17. November 2009, 23:27

Multiserver (TCP)

Hi,

ich habe mal ein einfaches Beispiel - oder eher eine Grundlage - für einen stabilen TCP-MultiServer programmiert, jeder Client bekommt dabei einen eigenen Thread zugewiesen. Hoffentlich lehrreich und ausreichend kommentiert.

Download: MultiServer Sample.7z

Im folgenden der Code noch einmal direkt gepostet.

Der MultiServer (Konsolenanwendung)

Visual Basic Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
Imports System.Net.Sockets
Imports System.IO
Imports System.Net

' TCP-MultiServer 
' C 2009 - Vincent Casser

Module Module1
    Private server As TcpListener
    Private client As New TcpClient
    Private ipendpoint As IPEndPoint = New IPEndPoint(IPAddress.Any, 8000) ' eingestellt ist port 8000. dieser muss ggf. freigegeben sein!
    Private list As New List(Of Connection)

    Private Structure Connection
        Dim stream As NetworkStream
        Dim streamw As StreamWriter
        Dim streamr As StreamReader
        Dim nick As String ' natürlich optional, aber für die identifikation des clients empfehlenswert.
    End Structure

    Private Sub SendToAllClients(ByVal s As String)
        For Each c As Connection In list ' an alle clients weitersenden.
            Try
                c.streamw.WriteLine(s)
                c.streamw.Flush()
            Catch
            End Try
        Next
    End Sub

    Sub Main()
        Console.WriteLine("Der Server läuft!")
        server = New TcpListener(ipendpoint)
        server.Start()

        While True ' wir warten auf eine neue verbindung...
            client = server.AcceptTcpClient

            Dim c As New Connection ' und erstellen für die neue verbindung eine neue connection...
            c.stream = client.GetStream
            c.streamr = New StreamReader(c.stream)
            c.streamw = New StreamWriter(c.stream)

            c.nick = c.streamr.ReadLine ' falls das mit dem nick nicht gewünscht, auch diese zeile entfernen.

            list.Add(c) ' und fügen sie der liste der clients hinzu.
            Console.WriteLine(c.nick & " has joined.")
            ' falls alle anderen das auch lesen sollen können, an alle clients weiterleiten. siehe SendToAllClients

            Dim t As New Threading.Thread(AddressOf ListenToConnection)
            t.Start(c)
        End While
    End Sub

    Private Sub ListenToConnection(ByVal con As Connection)
        Do
            Try
                Dim tmp As String = con.streamr.ReadLine ' warten, bis etwas empfangen wird...
                Console.WriteLine(con.nick & ": " & tmp)
                SendToAllClients(con.nick & ": " & tmp) ' an alle clients weitersenden.

            Catch ' die aktuelle überwachte verbindung hat sich wohl verabschiedet.
                list.Remove(con)
                Console.WriteLine(con.nick & " has exit.")
                Exit Do
            End Try
        Loop
    End Sub
End Module


Ein einfacher Beispiel-Client (Windows-Forms-Anwendung)

- Eine Listbox für die Auflistung der Nachrichten.
- Eine Textbox für die Nachricht
- Ein Button für's absenden.

Visual Basic Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Imports System.Net.Sockets
Imports System.IO

Public Class Form1
    Private stream As NetworkStream
    Private streamw As StreamWriter
    Private streamr As StreamReader
    Private client As New TcpClient
    Private t As New Threading.Thread(AddressOf Listen)
    Private Delegate Sub DAddItem(ByVal s As String)
    Private nick As String = "unknown"

    Private Sub AddItem(ByVal s As String)
        ListBox1.Items.Add(s)
    End Sub

    Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        Try
            client.Connect("127.0.0.1", 8000) ' hier die ip des servers eintragen. 
            ' da dieser beim testen wohl lokal läuft, hier die loopback-ip 127.0.0.1.
            If client.Connected Then
                stream = client.GetStream
                streamw = New StreamWriter(stream)
                streamr = New StreamReader(stream)

                streamw.WriteLine(nick) ' das ist optional.
                streamw.Flush()

                t.Start()
            Else
                MessageBox.Show("Verbindung zum Server nicht möglich!")
                Application.Exit()
            End If
        Catch ex As Exception
            MessageBox.Show("Verbindung zum Server nicht möglich!")
            Application.Exit()
        End Try
    End Sub

    Private Sub Listen()
        While client.Connected
            Try
                Me.Invoke(New DAddItem(AddressOf AddItem), streamr.ReadLine)
            Catch
                MessageBox.Show("Verbindung zum Server nicht möglich!")
                Application.Exit()
            End Try
        End While
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        streamw.WriteLine(TextBox1.Text)
        streamw.Flush()
        TextBox1.Clear()
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        nick = InputBox("Nickname: ", "Namen festlegen", "unknown")
    End Sub
End Class


Mit dem MultiServer-Code als Vorlage lässt sich schon einiges anstellen. Der Server muss natürlich immer laufen. Viel Spaß dabei, bitte kein reines C&P betreiben sondern den Code ansehen und verstehen (!), bei Fragen antworten.

Der Code darf ohne Genehmigung nicht anderweitig publiziert werden.

LG

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »kevin89« (19. Dezember 2009, 18:35)


Taix

Registrierter Benutzer

  • »Taix« ist männlich

Beiträge: 52

Dabei seit: 28. September 2009

Wohnort: Velbert

  • Private Nachricht senden

2

Mittwoch, 18. November 2009, 16:59

Finde ich gut, dass du jetzt auch einen Multi-Server-Tutorial gemacht hast :) Werde es späer ausprobieren,
kann man dafür auch den Client aus dem anderen tutorial verwenden?

// Edit :
Klappt prima, muss nur noch im Client ein zusätzliches Feld für den Nick einfügen :)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Taix« (18. November 2009, 21:34)


kevin89

Super Moderator

  • »kevin89« ist männlich
  • »kevin89« ist der Autor dieses Themas

Beiträge: 3 881

Dabei seit: 16. April 2008

Hilfreich-Bewertungen: 155

  • Private Nachricht senden

3

Mittwoch, 18. November 2009, 18:51

Theoretisch schon, wenn der Client auf die richtige IP (die IP des Servers) und den richtigen Port eingestellt ist. Das mit dem Nickname sollte man dann aber entweder beim Server rausnehmen oder beim anderen Client noch einbauen.

  • »Waijkey« ist männlich

Beiträge: 183

Dabei seit: 15. Oktober 2009

Wohnort: Wendlingen

Hilfreich-Bewertungen: 10

  • Private Nachricht senden

4

Mittwoch, 18. November 2009, 22:27

Als was muss ich Messagebox und Application deklarieren?!


CodeMonkey v 1.0: 70%

kevin89

Super Moderator

  • »kevin89« ist männlich
  • »kevin89« ist der Autor dieses Themas

Beiträge: 3 881

Dabei seit: 16. April 2008

Hilfreich-Bewertungen: 155

  • Private Nachricht senden

5

Mittwoch, 18. November 2009, 22:29

Der Client ist eine Windows-Forms-Anwendung... System.Windows.Forms.MessageBox und und System.Windows.Forms.Application sollten also zur Verfügung stehen.

  • »Waijkey« ist männlich

Beiträge: 183

Dabei seit: 15. Oktober 2009

Wohnort: Wendlingen

Hilfreich-Bewertungen: 10

  • Private Nachricht senden

6

Mittwoch, 18. November 2009, 22:32

Nein :)

Der Name "MessageBox" wurde nicht deklariert.
Der Name "Application" wurde nicht deklariert.


CodeMonkey v 1.0: 70%

CrashWriter

Registrierter Benutzer

Beiträge: 21

Dabei seit: 20. August 2009

  • Private Nachricht senden

7

Donnerstag, 19. November 2009, 07:57

ein großes dankeschön an dich

8

Donnerstag, 19. November 2009, 12:21

Ersteinmal Dankeschön für dieses schöne Beispiel eines MultiUserChats. Hab da allerdings ein paar Fragen: Ich selber möchte auch einen Chat basteln, jedoch einen wo der Server nicht als Konsole sondern auch als Windows Form Anwendung läuft. Jetzt hab ich da ja das Problem mit der Endlosschleife, dass ich keine anderen Benutzereingaben mehr abfangen kann. Wäre es nicht grundsätzlich auch besser (sorry wenn ich jetzt was erfrage was es nicht gibt ;-)) einen onEvent abzufangen?

Wenn es diesen gibt, frag ich mich als kleiner Hobbyprogrammierer wo ich das bei den Sockets finde...

Viele Grüße
Thomas

kevin89

Super Moderator

  • »kevin89« ist männlich
  • »kevin89« ist der Autor dieses Themas

Beiträge: 3 881

Dabei seit: 16. April 2008

Hilfreich-Bewertungen: 155

  • Private Nachricht senden

9

Donnerstag, 19. November 2009, 12:28

Hi,

die Konsolenanwendung habe ich einfach aus dem Grund gewählt, weil der Server ja grundsätzlich kein GUI braucht. Natürlich kannst du den Server auch in eine Windows-Forms-Anwendung packen. Das Grundproblem ist nicht die Endlosschleife (die sich übrigens nur nach jeder neuen Verbindung in der Ausführung wiederholt), sondern die Stelle, wo so lange mit der Ausführung gewartet wird, bis eine neue Verbindung eingeht. Resultat ist eine Form ohne Reaktion, die also "einfriert".

Die Stelle mit dem Warten auf eine Verbindung kannst du daher einfach in einen separaten Thread packen. Somit sind auch weitere Benutzereingaben möglich. Bei der Konsole war das nicht nötig, bei einer Windows-Forms-Anwendung ist es das durchaus.

LG

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »kevin89« (19. November 2009, 12:35)


Boolean

Registrierter Benutzer

Beiträge: 291

  • Private Nachricht senden

10

Donnerstag, 19. November 2009, 15:58

Oder den Client als Konsole lassen und einfach per Messages mit einer GUI Form kommunizieren lassen, die nur gestartet wird wenn sie benötigt wird...
Und nicht vergessen die Verbindung vom Client auch beenden zu lassen, in dem Code hier ist ja nichts dergleichen vorhanden.
Also wer da CnP macht fällt schnell auf die Nase :P

Littleboy2009

^H!tMaN'o_O!

  • »Littleboy2009« ist männlich

Beiträge: 574

Dabei seit: 29. April 2009

Hilfreich-Bewertungen: 3

  • Private Nachricht senden

11

Donnerstag, 19. November 2009, 19:57

supi hat mir übelste geholfen xDD DANKE :thumbsup:

Patschi

pkern.at

  • »Patschi« ist männlich

Beiträge: 165

Dabei seit: 31. Juli 2009

Wohnort: pkern.at / Österreich

  • Private Nachricht senden

12

Samstag, 21. November 2009, 12:43

Funktioniert einwandfrei!

Hilft mir sehr danke!
Meine neue Homepage: pkern.at
Wetter bei mir zu Haus:

13

Samstag, 21. November 2009, 15:54

Hi,

die Konsolenanwendung habe ich einfach aus dem Grund gewählt, weil der Server ja grundsätzlich kein GUI braucht. Natürlich kannst du den Server auch in eine Windows-Forms-Anwendung packen. Das Grundproblem ist nicht die Endlosschleife (die sich übrigens nur nach jeder neuen Verbindung in der Ausführung wiederholt), sondern die Stelle, wo so lange mit der Ausführung gewartet wird, bis eine neue Verbindung eingeht. Resultat ist eine Form ohne Reaktion, die also "einfriert".

Die Stelle mit dem Warten auf eine Verbindung kannst du daher einfach in einen separaten Thread packen. Somit sind auch weitere Benutzereingaben möglich. Bei der Konsole war das nicht nötig, bei einer Windows-Forms-Anwendung ist es das durchaus.

LG
Hallo,

sorry das es bei mir etwas länger dauerte bis ich jetzt wieder dazu kam hab jetzt endlich meine Diplomprüfung zu ende gebracht. Aber zum eigentlichen Thema: Ich steh gerade etwas auf dem Kopf, möglicherweise wegen dem Threading.. werde mich da wohl mal einlesen müssen! So wie ich dich verstehe würde ich mit dem warten der Verbindung also : While True in einen Thread Packen wie du das jetzt mit ListenToConnection gemacht hast??? Heißt das die Dinge Parallel laufen?? Das wäre ja der Hammer...

Vielen Dank jetzt schon für den Denkanstoß!
Thomas

kevin89

Super Moderator

  • »kevin89« ist männlich
  • »kevin89« ist der Autor dieses Themas

Beiträge: 3 881

Dabei seit: 16. April 2008

Hilfreich-Bewertungen: 155

  • Private Nachricht senden

14

Samstag, 21. November 2009, 16:29

Hallo Thomas,

ja - so ungefähr. "Der Hammer" wäre es trotzdem nicht, denn der Server führt wirklich nur Code aus, wenn sich entweder ein Client verbindet, trennt, oder etwas sendet. Insofern ist der Server die meiste Zeit in einem "Schlaf-Zustand" und verbraucht damit praktisch keine Systemresourcen.

Hier ein Beispiel:

Visual Basic Quellcode

1
 Private mainthread As New System.Threading.Thread(AddressOf ListenToNewConnection)

Visual Basic Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 Private Sub ListenToNewConnection()
        Console.WriteLine("Der Server läuft!")
        server = New TcpListener(ipendpoint)
        server.Start()

        While True ' wir warten auf eine neue verbindung...
            client = server.AcceptTcpClient

            Dim c As New Connection ' und erstellen für die neue verbindung eine neue connection...
            c.stream = client.GetStream
            c.streamr = New StreamReader(c.stream)
            c.streamw = New StreamWriter(c.stream)

            c.nick = c.streamr.ReadLine ' falls das mit dem nick nicht gewünscht, auch diese zeile entfernen.

            list.Add(c) ' und fügen sie der liste der clients hinzu.
            Console.WriteLine(c.nick & " has joined.")
            ' falls alle anderen das auch lesen sollen können, an alle clients weiterleiten. siehe SendToAllClients

            Dim t As New Threading.Thread(AddressOf ListenToConnection)
            t.Start(c)
        End While
    End Sub

Visual Basic Quellcode

1
2
3
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        mainthread.Start()
    End Sub


Du siehst: Das einzige was ich zusätzlich geschrieben habe beträgt gerade mal 2 Zeilen und die dürften verständlich sein. Außerdem habe ich den Namen der Main-Prozedur abgeändert, was man wohl nicht als großen Akt bezeichnen könnte. So kompliziert ist das gar nicht. Nicht vergessen den Thread beim Schließen zu stoppen und die Verbindungen zu schließen. Mit diesem Code läuft das Warten auf neue Verbindungen in einem Extra-Thread und die Form lässt sich problemlos bedienen.

LG

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »kevin89« (21. November 2009, 16:35)


Es haben bereits 2 registrierte Benutzer diesen Beitrag als hilfreich eingestuft.

Benutzer, die diesen Beitrag hilfreich fanden:

Mister-X, Robin (Black-Knight)

15

Samstag, 21. November 2009, 16:57

Genau so hatte ich mir das gerade vorgestellt. Werde das jetzt mal in Ruhe ausprobieren und versuchen zu verstehen, wobei es auch nicht schwer ausschaut. Mit Threads hatte ich nur noch nie zu tun....

Danke!

Sakeco

Registrierter Benutzer

Beiträge: 136

Dabei seit: 14. Mai 2009

  • Private Nachricht senden

16

Samstag, 21. November 2009, 17:22

Bei allem Respekt, aber

Zitat

Der Code darf ohne Verweis nicht kopiert und ohne Genehmigung nicht anderweitig publiziert werden.
ein Uhrheberrechtsanspruch auf einen allgemeinen Code erheben, der wirklich nicht mehr als ein Grundgerüst ist?

kevin89

Super Moderator

  • »kevin89« ist männlich
  • »kevin89« ist der Autor dieses Themas

Beiträge: 3 881

Dabei seit: 16. April 2008

Hilfreich-Bewertungen: 155

  • Private Nachricht senden

17

Samstag, 21. November 2009, 17:25

Verwendet werden darf er natürlich. War vielleicht etwas unglücklich formuliert, gemeint war Kopieren nur im Bezug auf anderweitige Publizierung.

Sakeco

Registrierter Benutzer

Beiträge: 136

Dabei seit: 14. Mai 2009

  • Private Nachricht senden

18

Samstag, 21. November 2009, 17:44

Achso, na dann. Super code übrigens :)

Werde ich vielleicht mal verwenden.

Confix.npage.de

Mag Algorithmen :)

  • »Confix.npage.de« ist männlich

Beiträge: 988

Dabei seit: 30. Oktober 2009

Wohnort: Berlin

Frühere Benutzernamen: Confix.npage.de

Hilfreich-Bewertungen: 2

  • Private Nachricht senden

19

Sonntag, 22. November 2009, 17:07

Mal zu der Frage von VOrhin
Messagebox ist nicht deklariert
nenn sie einfach MsgBox
Falls dir geholfen wurde :thanks;

20

Sonntag, 22. November 2009, 22:36

Hallo,

also das funktioniert soweit alles spitze, die Form friert nicht ein, der Client kann sich sogar Connecten. Allerdings gibts bei den ganzen Dingen ein kleines Problem und zwar ist ja der Zugriff zwischen Threads wie ich gelesen habe nicht so ohne weiteres möglich. Ich hab das jetzt wie folgt gelöst:

Visual Basic Quellcode

1
Control.CheckForIllegalCrossThreadCalls = False


Aber ist das eine gute Möglichkeit oder würde das noch schöner gehn? Das nächste Problem ist ja das ich jetzt irgendwie mitbekommen muss wann sich ein Client "verabschiedet", disconnected etc... Oder soll(muss) man dies hier mit einer regelmäßigen Kontaktaufnahme selber herausfinden ob es den Clienten noch gibt? Als alte vb6 Idee hab ich da erstmal nur ein OnDisconnect Event???? Aber ich denke das geht in die falsche Richtung oder?

Vielen Dank für den Denkanstoß!

vg Thomas

Social Bookmarks