Fehlende Packets in TCP bei Asynchroner Kommunikation

luigied

Mitglied
Hi,

ich arbeite an einem Client-Server-Projekt. Da ich mit asynchronen Sockets in .NET noch nicht so firm bin, nutze ich als Kommunikationsbibliothek eine leicht angepasste Version der MSDN-Beispiel-Implementierungen
http://msdn.microsoft.com/de-de/library/fx6588te.aspx
http://msdn.microsoft.com/de-de/library/bew39x2a.aspx

Mir ist bewusst, dass dies eine möglichst minimalistische Implementierung zu sein scheint, aber kann mir bitte jemand sagen, an welchen Stelle die Nutzung dieses Codes problematisch werden könnte? Und warum?

Mein Bibliothek funktioniert 90% der Zeit problemlos, selbst bei "größeren" Dateimengen (~15 KB)

Allerdings habe ich im Moment das Problem, dass wenn ich eine bestimmte Aktion ausführe zwar die richtigen Daten gesendet werden. Diese aber nicht komplett ankommen. Es fehlt in diesem Fall scheinbar das 1. kB.

Hier mein kompletter Quelltext für die Bibliothek:
Code:
Public Class StateObject
    Public ID As Integer = 0
    ' Client  socket.
    Public workSocket As Socket = Nothing
    ' Size of receive buffer.
    Public Const BufferSize As Integer = 1024
    ' Receive buffer.
    Public buffer(BufferSize) As Byte
    ' Received data string.
    Public sb As New StringBuilder
End Class 'StateObject

Public Class AsyncronousServer
    Implements IDisposable

    Public allDone As New ManualResetEvent(False)   'informiert über ACCEPT
    Public MessageBuffer As Queue(Of Message)
    'Public clients As New Hashtable '(Client-ID, StateObject)
    Public state As New StateObject
    Public localSock As Socket
    Public MsgEvent As ManualResetEvent             ' wenn eine Nachricht erhalten wurde

    Public Sub New(ByVal listenEP As IPEndPoint, ByRef MBuffer As Queue(Of Message), ByRef newMsg As ManualResetEvent)
        localSock = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        localSock.Bind(listenEP)
        MessageBuffer = MBuffer
        MsgEvent = newMsg
        localSock.Listen(1)
    End Sub

    Public Sub Accept()
        localSock.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), localSock)
    End Sub


    Public Sub AcceptCallback(ByVal ar As IAsyncResult)
        ' Get the socket that handles the client request.

        Dim listener As Socket = CType(ar.AsyncState, Socket)
        ' End the operation.
        Dim handler As Socket = listener.EndAccept(ar)
        ' Create the state object for the async receive.

        state.workSocket = handler
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
        allDone.Set()
    End Sub 'AcceptCallback

    Public Sub read()
        state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
    End Sub

    Public Sub ReadCallback(ByVal ar As IAsyncResult)
        Dim content As String = String.Empty

        ' Retrieve the state object and the handler socket
        ' from the asynchronous state object.
        Dim state As StateObject = CType(ar.AsyncState, StateObject)
        Dim handler As Socket = state.workSocket

        ' Read data from the client socket. 
        Dim bytesRead As Integer = 0
        Try
            ' Read data from the remote device.
            bytesRead = handler.EndReceive(ar)
        Catch ex As Exception
            Console.WriteLine("Verbindung zum Server verloren")
        End Try
        Try
            If bytesRead > 0 Then
                ' There  might be more data, so store the data received so far.
                state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead))

                content = state.sb.ToString()
                If bytesRead < StateObject.BufferSize Then
                    ' All the data has been read from the 
                    ' client. Display it on the console.
                    Dim msg As New Message(state.sb.ToString())
                    MessageBuffer.Enqueue(msg)
                    MsgEvent.Set()
                    state.sb.Remove(0, state.sb.Length) 'speicher leeren
                Else
                    ' Not all data received. Get more.
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
                End If
            End If
        Catch ex As Exception
            Console.WriteLine("Fehler beim Lesen: " & ex.ToString())
        Finally

        End Try
    End Sub 'ReadCallback

    Public Sub Send(ByVal ID As Integer, ByVal data As String)
        ' Convert the string data to byte data using UTF8 encoding.
        Dim byteData As Byte() = Encoding.UTF8.GetBytes(data)

        ' Begin sending the data to the remote device.
        state.workSocket.BeginSend(byteData, 0, byteData.Length, 0, New AsyncCallback(AddressOf SendCallback), state)
    End Sub 'Send


    Private Sub SendCallback(ByVal ar As IAsyncResult)
        ' Retrieve the socket from the state object.
        Dim handler As Socket = CType(ar.AsyncState.workSocket, Socket)

        ' Complete sending the data to the remote device.
        Dim bytesSent As Integer = handler.EndSend(ar)
        read() ' wieder alles auf Lesen setzen
    End Sub 'SendCallback

    Public Sub closeAll()
        state.workSocket.Shutdown(SocketShutdown.Both)
        state.workSocket.Close()
    End Sub

#Region "IDisposable Support"
    Private disposedValue As Boolean ' So ermitteln Sie überflüssige Aufrufe

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                state.workSocket.Shutdown(SocketShutdown.Both)
                state.workSocket.Close()
            End If

            ' TODO: Nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() unten überschreiben.
            ' TODO: Große Felder auf NULL festlegen.
        End If
        Me.disposedValue = True
    End Sub

    ' Dieser Code wird von Visual Basic hinzugefügt, um das Dispose-Muster richtig zu implementieren.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Ändern Sie diesen Code nicht. Fügen Sie oben in Dispose(ByVal disposing As Boolean) Bereinigungscode ein.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

Public Class AsyncronousClient
    Implements IDisposable

    Public socktoserver As Socket

    ' ManualResetEvent instances signal completion.
    Private Shared connectDone As New ManualResetEvent(False)
    Private Shared sendDone As New ManualResetEvent(False)
    Private Shared receiveDone As New ManualResetEvent(False)
    Private newMsg As ManualResetEvent
    Private content As New StringBuilder()

    Private MessageBuffer As Queue(Of Message)

    Public Sub New(ByVal sock As Socket, ByRef MBuffer As Queue(Of Message), ByRef MsgEvent As ManualResetEvent)
        socktoserver = sock
        MessageBuffer = MBuffer
        newMsg = MsgEvent
    End Sub

    Public Function connect(ByVal remoteEP As EndPoint) As Boolean
        socktoserver.BeginConnect(remoteEP, New AsyncCallback(AddressOf ConnectCallback), socktoserver)
        connectDone.WaitOne()
        Return socktoserver.Connected
    End Function

    Private Sub ConnectCallback(ByVal ar As IAsyncResult)
        ' Retrieve the socket from the state object.
        Dim client As Socket = CType(ar.AsyncState, Socket)

        ' Complete the connection.
        client.EndConnect(ar)

        'Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString())

        ' Signal that the connection has been made.
        connectDone.Set()
    End Sub 'ConnectCallback


    Public Sub Receive()

        ' Create the state object.
        Dim state As New StateObject
        state.workSocket = socktoserver

        ' Begin receiving the data from the remote device.
        socktoserver.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
    End Sub 'Receive


    Private Sub ReceiveCallback(ByVal ar As IAsyncResult)

        ' Retrieve the state object and the client socket 
        ' from the asynchronous state object.
        Dim state As StateObject = CType(ar.AsyncState, StateObject)
        Dim client As Socket = state.workSocket
        Dim bytesRead As Integer = 0
        Try
            ' Read data from the remote device.
            bytesRead = client.EndReceive(ar)
        Catch ex As Exception
            Console.WriteLine("Verbindung zum Server verloren")
        End Try
        If bytesRead > 0 Then
            ' There might be more data, so store the data received so far.
            state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead))

            content.Append(state.sb.ToString())
            state.sb.Remove(0, state.sb.Length) 'speicher leeren
            If bytesRead < StateObject.BufferSize Then
                ' All the data has been read from the 
                ' client. Display it on the console.
                Dim msg As New Message(content.ToString())
                MessageBuffer.Enqueue(msg)
                'state.sb.Remove(0, state.sb.Length) 'speicher leeren
                content.Remove(0, content.Length)
                newMsg.Set()
                Receive()
            Else
                ' Not all data received. Get more.
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
            End If
            'newMsg.Reset()
        End If
    End Sub 'ReceiveCallback


    Public Sub Send(ByVal data As String)
        ' Convert the string data to byte data using UTF8 encoding.
        Dim byteData As Byte() = Encoding.UTF8.GetBytes(data)

        ' Begin sending the data to the remote device.
        socktoserver.BeginSend(byteData, 0, byteData.Length, 0, New AsyncCallback(AddressOf SendCallback), socktoserver)
    End Sub 'Send


    Private Sub SendCallback(ByVal ar As IAsyncResult)
        ' Retrieve the socket from the state object.
        Dim client As Socket = CType(ar.AsyncState, Socket)

        ' Complete sending the data to the remote device.
        Dim bytesSent As Integer = client.EndSend(ar)

        ' Signal that all bytes have been sent.
        sendDone.Set()
        Receive()   ' wieder auf Lesen setzen
    End Sub 'SendCallback

    Public Sub close()
        socktoserver.Shutdown(SocketShutdown.Both)
        socktoserver.Close()
    End Sub

#Region "IDisposable Support"
    Private disposedValue As Boolean ' So ermitteln Sie überflüssige Aufrufe

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                close()
            End If

            ' TODO: Nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() unten überschreiben.
            ' TODO: Große Felder auf NULL festlegen.
        End If
        Me.disposedValue = True
    End Sub

    ' Dieser Code wird von Visual Basic hinzugefügt, um das Dispose-Muster richtig zu implementieren.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Ändern Sie diesen Code nicht. Fügen Sie oben in Dispose(ByVal disposing As Boolean) Bereinigungscode ein.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

Ich habe mir auch schon mehrere Tutorials online angesehen. Diese behandeln meist aber nur die Grundlagen, wie im MSDN-Beispiel.

Danke schonmal für die Hilfe.

mfg LuigiEd
 
Zurück