TCP/IP Async multiserver, extrem komsiches Problem mit String

  • VB.NET

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von schockerjo.

    TCP/IP Async multiserver, extrem komsiches Problem mit String

    Ich habe ein , meiner Meinung nach, extrem merkwürdiges Problem mit einem, von mir modifizierten, VersuchsChat mit leistungsfähigem Server .
    Ich
    habe ein Event das ausgelöst wird, wenn einer der Clients eine Naricht
    bekommt. Und jedes Client Objekt hat ein Objekt, das ein paar Infos
    besitzt. Soweit so gut, nur das Problem ist, dass wenn ich z.B.

    VB.NET-Quellcode

    1. Info.Nickname & ":" & Naricht
    schreibe kommt einfach nur der Nickname und die Nachricht wird sozusagen abgeschnitten.

    Hier mal der "wichtige" Code:

    Client
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Net.Sockets
    2. Imports System.Text
    3. Imports TCPAsynServer.Disposer
    4. Imports TCPAsynServer.EventArgs
    5. Imports System.Net
    6. Public Class Client : Implements IDisposable
    7. Private _TcpClient As TcpClient
    8. Private _Stream As NetworkStream
    9. Private Buf(&H400 - 1) As Byte
    10. Public settingsObject As Object
    11. Public ReadOnly Property TcpClient As TcpClient
    12. Get
    13. Return _TcpClient
    14. End Get
    15. End Property
    16. Public Sub New(ByVal TC As TcpClient)
    17. _TcpClient = TC
    18. _Stream = _TcpClient.GetStream
    19. End Sub
    20. Public Sub New(ByVal IP As Long, ByVal Port As UShort)
    21. Me.New(New TcpClient(New IPEndPoint(IP, Port)))
    22. End Sub
    23. Public Sub New(ByVal _IPEndPoint As IPEndPoint)
    24. Me.New(New TcpClient(_IPEndPoint))
    25. End Sub
    26. Private Sub EndRead(ByVal ar As IAsyncResult)
    27. If Me.IsDisposed Then Return
    28. Dim read As Integer = 0
    29. Try
    30. read = _Stream.EndRead(ar)
    31. Catch
    32. CrossThread.RunGui(AddressOf Dispose)
    33. Return
    34. End Try
    35. If read = 0 Then
    36. CrossThread.RunGui(AddressOf Dispose)
    37. Return
    38. End If
    39. Dim readArray As Byte() = Buf
    40. Do While _Stream.DataAvailable
    41. read = _Stream.Read(Buf, 0, Buf.Length)
    42. readArray(readArray.Length) = CByte(read)
    43. Loop
    44. Dim NewMessageArgs As New NewMessageEventargs(Me, readArray.ToArray)
    45. CrossThread.RunGui(AddressOf OnNewMessage, NewMessageArgs)
    46. Dim tempar(&H400 - 1) As Byte
    47. Buf = tempar
    48. _Stream.BeginRead(Buf, 0, Buf.Length, AddressOf EndRead, Nothing)
    49. End Sub
    50. Public Sub Dispose() Implements IDisposable.Dispose
    51. If _IsDisposed Then Return
    52. _IsDisposed = True
    53. DisposeAll(_Stream, _TcpClient)
    54. CrossThread.RunGui(AddressOf OnDisposed)
    55. GC.SuppressFinalize(Me)
    56. End Sub
    57. Public Sub [Stop]()
    58. Me.Dispose()
    59. End Sub
    60. Public Sub Start()
    61. If _TcpClient.Client.RemoteEndPoint.ToString = "" Then
    62. Throw New NullReferenceException("You tried to start an empty
    63. 'Client'-object. Please, use 'Sub New' and its overloads to create an
    64. instance of the class.")
    65. Else
    66. _Stream.BeginRead(Buf, 0, Buf.Length, AddressOf EndRead, Nothing)
    67. End If
    68. End Sub
    69. #Region "SendOverloads"
    70. Public Sub Send(ByVal Content As Byte())
    71. _Stream.Write(Content, 0, Content.Length)
    72. End Sub
    73. Public Sub Send(ByVal Content As String)
    74. Dim Buffer() As Byte = Encoding.UTF8.GetBytes(Content)
    75. Send(Buffer)
    76. End Sub
    77. #End Region
    78. #Region "Events"
    79. Private _IsDisposed As Boolean = False
    80. Public ReadOnly Property IsDisposed() As Boolean
    81. Get
    82. Return _IsDisposed
    83. End Get
    84. End Property
    85. Public Event Disposed(ByVal sender As Object)
    86. Protected Sub OnDisposed()
    87. RaiseEvent Disposed(Me)
    88. End Sub
    89. Public Event NewMessage(ByVal sender As Object, ByVal e As NewMessageEventargs)
    90. Protected Sub OnNewMessage(ByVal e As NewMessageEventargs)
    91. RaiseEvent NewMessage(Me, e)
    92. End Sub
    93. #End Region
    94. End Class


    Settingsklasse

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Net.Sockets
    2. Imports System.Text
    3. Imports TCPAsynServer.EventArgs
    4. Imports System.Net
    5. Public Class CustomClientSettings
    6. Protected _Nickname As String
    7. Public Property Nickname As String
    8. Get
    9. Return _Nickname
    10. End Get
    11. Set(ByVal value As String)
    12. _Nickname = value
    13. End Set
    14. End Property
    15. Protected _IP As IPEndPoint
    16. Public ReadOnly Property getIP As String
    17. Get
    18. Return _IP.Address.ToString
    19. End Get
    20. End Property
    21. Public Property IP As IPEndPoint
    22. Get
    23. Return _IP
    24. End Get
    25. Set(ByVal value As IPEndPoint)
    26. _IP = value
    27. End Set
    28. End Property
    29. Protected _initialised As Boolean
    30. Public Property initialised As Boolean
    31. Get
    32. Return _initialised
    33. End Get
    34. Set(ByVal value As Boolean)
    35. _initialised = value
    36. End Set
    37. End Property
    38. End Class


    EventArgs

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class EventArgs
    2. Public Class NewMessageEventargs : Inherits EventArgs
    3. Public ReadOnly Message As Byte()
    4. Public ReadOnly Client As Client
    5. Public Sub New(ByVal sender As Client, ByVal Content() As Byte)
    6. MyBase.New()
    7. Me.Message = Content
    8. Me.Client = sender
    9. End Sub
    10. End Class
    11. Public Class ClientNewStatusEventArgs : Inherits EventArgs
    12. Public ReadOnly Client As Client
    13. Public Sub New(ByVal c As Client)
    14. MyBase.New()
    15. Me.Client = c
    16. End Sub
    17. End Class
    18. End Class


    Server

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Net.Sockets
    2. Imports System.Net
    3. Imports TCPAsynServer.EventArgs
    4. Public Class Server
    5. Private _Listener As TcpListener
    6. Private _Clients As New List(Of Client)
    7. Public Sub New(ByVal EP As IPEndPoint)
    8. _Listener = New TcpListener(EP)
    9. _Listener.ExclusiveAddressUse = False
    10. End Sub
    11. Public Sub Start()
    12. _Listener.Start()
    13. _Listener.BeginAcceptTcpClient(AddressOf EndAccept, Nothing)
    14. End Sub
    15. Private Sub EndAccept(ByVal ar As IAsyncResult)
    16. If _IsDisposed Then Return
    17. Dim c As New Client(_Listener.EndAcceptTcpClient(ar))
    18. With c
    19. AddHandler .NewMessage, AddressOf Client_OnNewMessage
    20. AddHandler .Disposed, AddressOf Client_OnDispose
    21. _Clients.Add(c)
    22. Client_OnConnect(c)
    23. .Start()
    24. End With
    25. _Listener.BeginAcceptTcpClient(AddressOf EndAccept, Nothing)
    26. End Sub
    27. Protected Sub Dispose()
    28. _Listener.Stop()
    29. For i As Integer = _Clients.Count - 1 To 0 Step -1
    30. _Clients(i).Dispose()
    31. Next
    32. _IsDisposed = True
    33. CrossThread.RunGui(AddressOf OnDisposed)
    34. End Sub
    35. Public Sub [Stop]()
    36. Dispose()
    37. End Sub
    38. #Region "Server_Events"
    39. Private _IsDisposed As Boolean = False
    40. Public ReadOnly Property IsDisposed() As Boolean
    41. Get
    42. Return _IsDisposed
    43. End Get
    44. End Property
    45. Public Event Disposed(ByVal sender As Object)
    46. Protected Sub OnDisposed()
    47. RaiseEvent Disposed(Me)
    48. End Sub
    49. #End Region
    50. #Region "Client_Events"
    51. Public Event Client_Disposed(ByVal sender As Object, ByVal e As ClientNewStatusEventArgs)
    52. Protected Sub OnClient_Disposed(ByVal e As ClientNewStatusEventArgs)
    53. RaiseEvent Client_Disposed(Me, e)
    54. End Sub
    55. Public Event Client_Connected(ByVal sender As Object, ByVal e As ClientNewStatusEventArgs)
    56. Protected Sub OnClient_Connected(ByVal e As ClientNewStatusEventArgs)
    57. RaiseEvent Client_Connected(Me, e)
    58. End Sub
    59. Public Event Client_NewMessage(ByVal sender As Object, ByVal e As NewMessageEventargs)
    60. Protected Sub OnClient_NewMessage(ByVal e As NewMessageEventargs)
    61. RaiseEvent Client_NewMessage(Me, e)
    62. End Sub
    63. Protected Sub Client_OnDispose(ByVal sender As Object)
    64. CrossThread.RunGui(AddressOf OnClient_Disposed, New ClientNewStatusEventArgs(DirectCast(sender, Client)))
    65. End Sub
    66. Protected Sub Client_OnConnect(ByVal sender As Object)
    67. CrossThread.RunGui(AddressOf OnClient_Connected, New ClientNewStatusEventArgs(DirectCast(sender, Client)))
    68. End Sub
    69. Protected Sub Client_OnNewMessage(ByVal sender As Object, ByVal e As NewMessageEventargs)
    70. CrossThread.RunGui(AddressOf OnClient_NewMessage, e)
    71. End Sub
    72. #End Region
    73. End Class


    Form1 (zum Testen des Codes)

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Net
    2. Imports System.Text
    3. Public Class Form1
    4. Dim WithEvents s As Server
    5. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    6. s = New Server(New System.Net.IPEndPoint(IPAddress.Any, 8900))
    7. s.Start()
    8. End Sub
    9. Private Sub s_Client_Connected(ByVal sender As Object, ByVal e As
    10. EventArgs.ClientNewStatusEventArgs) Handles s.Client_Connected
    11. StatusMessage("Client connected")
    12. e.Client.settingsObject = New CustomClientSettings
    13. DirectCast(e.Client.settingsObject, CustomClientSettings).IP =
    14. CType(e.Client.TcpClient.Client.RemoteEndPoint, Net.IPEndPoint)
    15. End Sub
    16. Private Sub s_Client_Disposed(ByVal sender As Object, ByVal e As
    17. EventArgs.ClientNewStatusEventArgs) Handles s.Client_Disposed
    18. Dim settings As CustomClientSettings = CType(e.Client.settingsObject, CustomClientSettings)
    19. StatusMessage(settings.Nickname & " disconnected")
    20. End Sub
    21. Private Sub s_Client_NewMessage(ByVal sender As Object, ByVal e As
    22. EventArgs.NewMessageEventargs) Handles s.Client_NewMessage
    23. Dim settings As CustomClientSettings = CType(e.Client.settingsObject, CustomClientSettings)
    24. Dim messageToString As String = Encoding.UTF8.GetString(e.Message)
    25. If settings.initialised = False Then
    26. Dim split() As String = messageToString.Split(CChar("|")) 'Später mache ich das dann mit Serialisierung
    27. settings.Nickname = split(0)
    28. settings.initialised = True
    29. e.Client.settingsObject = settings
    30. Else
    31. Dim nickname As String = settings.Nickname
    32. StatusMessage(nickname & ": " & messageToString) 'Ergebenis: nickname
    33. End If
    34. End Sub
    35. Private Sub s_Disposed(ByVal sender As Object) Handles s.Disposed
    36. StatusMessage("Server stopped")
    37. End Sub
    38. Private Sub StatusMessage(ByVal message As String)
    39. RichTextBox1.Text &= vbNewLine & message
    40. End Sub
    41. End Class


    Wäre super, wenn mir jemand helfen könnte, da ich schon den ganzen Abend an diesem Problem hänge :S
    Vielleicht ist der String ja einfach verflucht, wer weiß =O

    MfG :)

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „schockerjo“ ()

    Guck mal in Form1, beim Client_NewMessageEvent.

    Und hier wo die Nachricht im Client empfangen wird:

    VB.NET-Quellcode

    1. Private Sub EndRead(ByVal ar As IAsyncResult)
    2. If Me.IsDisposed Then Return
    3. Dim read As Integer = 0
    4. Try
    5. read = _Stream.EndRead(ar)
    6. Catch
    7. CrossThread.RunGui(AddressOf Dispose)
    8. Return
    9. End Try
    10. If read = 0 Then
    11. CrossThread.RunGui(AddressOf Dispose)
    12. Return
    13. End If
    14. Dim readArray As Byte() = Buf
    15. Do While _Stream.DataAvailable
    16. read = _Stream.Read(Buf, 0, Buf.Length)
    17. readArray(readArray.Length) = CByte(read)
    18. Loop
    19. Dim NewMessageArgs As New NewMessageEventargs(Me, readArray.ToArray)
    20. CrossThread.RunGui(AddressOf OnNewMessage, NewMessageArgs)
    21. Dim tempar(&H400 - 1) As Byte
    22. Buf = tempar
    23. _Stream.BeginRead(Buf, 0, Buf.Length, AddressOf EndRead, Nothing)
    24. End Sub


    Aber schon mal danke, dass du dich überhaupt einliest :thumbup:


    MfG

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „schockerjo“ ()

    Keine Ahnung, ob ich das falsch verstehe, aber in dem Beispiel handelt es sich nur um Strings. Die Nachricht wird konvertiert und der Nickname ist , ja, "as string"
    Und die Testausgabe erfolgt ja auch als String ?(

    Ausgabe
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub StatusMessage(ByVal message As String)
    2. RichTextBox1.Text &= vbNewLine & message
    3. End Sub



    NewMessage Event
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub s_Client_NewMessage(ByVal sender As Object, ByVal e As
    2. EventArgs.NewMessageEventargs) Handles s.Client_NewMessage
    3. Dim settings As CustomClientSettings = CType(e.Client.settingsObject, CustomClientSettings)
    4. Dim messageToString As String = Encoding.UTF8.GetString(e.Message)
    5. If settings.initialised = False Then
    6. Dim split() As String = messageToString.Split(CChar("|")) 'Später mache ich das dann mit Serialisierung
    7. settings.Nickname = split(0)
    8. settings.initialised = True
    9. e.Client.settingsObject = settings
    10. Else
    11. Dim nickname As String = settings.Nickname
    12. StatusMessage(nickname & ": " & messageToString) 'Ergebenis: nickname
    13. End If
    14. End Sub



    MfG
    Du verstehst es glaube ich nicht, ich will keine Strings versenden. Soweit ich weiß, ist das unsauber.(Lasse mich auch gerne eines besseren belehren) Das Beispiel war nur mit Strings, weil ich die Funktionen getestet habe, fürs Clientinformationen lesen.

    Nochmal, um das ganze etwas konkreter zu gestalten: Weißt du/ Weiß jemand, warum ein String den kompletten String "kaputt"schneiden kann ?

    MfG
    Meintest du hier ?

    VB.NET-Quellcode

    1. Dim settings As CustomClientSettings = CType(e.Client.settingsObject, CustomClientSettings)
    2. Dim messageToString As String = Encoding.Default.GetString(e.Message, 0, e.Message.Length) 'Hab mal das Split enfernt, weil ich dachte, dass es daran liegt :(
    3. If settings.initialised = False Then
    4. settings.Nickname = messageToString 'Später mache ich das dann mit Serialisierung
    5. settings.initialised = True
    6. e.Client.settingsObject = settings
    7. Else
    8. Dim nickname As String = settings.Nickname
    9. StatusMessage(nickname & ": " & messageToString) 'Ergebenis: nickname :/
    10. End If


    Wenn ja, das hat leider nicht geholfen. Aber trotzdem danke !

    Und die Sache, die ich nicht verstehe ist die: Alle anderen Strings die versenden werden funktionieren problemlos, nur halt der in den Settings gespeicherte nicht

    MfG
    Hab den Fehler gefunden, aber leider krieg ich ihn nicht behoben :/.

    Wenn ich eine Nachricht bekomme, benutzte ich ja einen Buffer. Und wenn ich mir dann die Länge des Temporärenarrays zum Lesen ausgeben lasse, ist diese immer 1024. Ich glaub, dort liegt der Fehler. Meine Frage: Soll ich das Array einfach kürzen oder ist das zu Perfomance lastig (Darauf war der Code speziell ausgelegt)

    Spoiler anzeigen
    Hier mal der Code:

    VB.NET-Quellcode

    1. Private Buf(&H400 - 1) As Byte
    2. Private Sub EndRead(ByVal ar As IAsyncResult)
    3. If Me.IsDisposed Then Return
    4. Dim read As Integer = 0
    5. Try
    6. read = _Stream.EndRead(ar)
    7. Catch
    8. CrossThread.RunGui(AddressOf Dispose)
    9. Return
    10. End Try
    11. If read = 0 Then
    12. CrossThread.RunGui(AddressOf Dispose)
    13. Return
    14. End If
    15. Dim readArray() As Byte = Buf '
    16. Do While _Stream.DataAvailable
    17. read = _Stream.Read(Buf.ToArray, 0, Buf.Length)
    18. readArray(readArray.Count) = CByte(read)
    19. Loop
    20. Dim NewMessageArgs As New NewMessageEventargs(Me, readArray.ToArray) 'Readarray hat immer die Größe des Buffers :/
    21. CrossThread.RunGui(AddressOf OnNewMessage, NewMessageArgs)
    22. Dim tempar(&H400 - 1) As Byte 'Ist das sehr Perfomance lastig jedesmal ein neues Array zu erstellen ?
    23. Buf = tempar
    24. _Stream.BeginRead(Buf, 0, Buf.Length, AddressOf EndRead, Nothing) 'Wieder von vorne Anfangen, sollte ich hier lieber ein Array mit der Größe des Buffers erstellen und das benutzen,damit ich mir das oben mit dem neuen erstelle spare ?
    25. End Sub


    MfG und Danke für alle Antworten
    Hat niemand eine Idee ? :(

    Hab hier mal weiter rumprobiert, Fehler steht als Kommentar im Code

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Dim buffer As Byte() = New Byte(1024) {}
    2. ns.BeginRead(buffer, 0, buffer.Length, New AsyncCallback(AddressOf ReadComplete), buffer)
    3. Sub ReadComplete(ByVal ar As IAsyncResult)
    4. If ns.CanRead = False Then
    5. Exit Sub
    6. End If
    7. Dim ar_buffer() As Byte = ar.AsyncState
    8. Dim bytesRead As Integer = ns.EndRead(ar)
    9. 'Hier hätte man entsprechend Array + Länge, nur das Problem ist, dass 1. der Code bei mir nicht funktioniert "NetworkStream.EndRead kann pro asynchronem Vorgang nur einmal ausgeführt werden" und 2. Würde ich das ganze gerne mit Fortschritts Event machen, nur so geht das ja nicht, weil die While-Schleife wegfällt.
    10. ns.BeginRead(ar_buffer, 0, ar_buffer.Length, AddressOf ReadComplete, ar_buffer)
    11. End Sub