Oppure

Loading
Questo topic e' stato chiuso dal moderatore.
04/02/11 23:27
Question
EDIT:ho risolto inserendo un try catch nei due thread di controllo (ho pensato di non poter prevedere quando l'utente chiude i socket) che non fà semplicemente nulla xD
Se avete una soluzione migliore mandatemi un PM o rispondete qua per favore ^^


Ciao a tutti ragazzi,
starei scrivendo un miglioramento di una classe che avevo fatto tempo fà con i TCP socket (TcpClient e TcpListener per intenderci).
Il miglioramento consiste nell'eliminare i numerosi backgroundworker che venivano generati a favore del multithreading, il problema è che ricevo un errore riguardo alla cancellazione di un'instanza nel thread di controllo dati che però dovrebbe ancora esistere :d

Il sorgente della classe è tutto commentato (in un inglese elementare) e non dovrebbe dare grossi problemi,comunque funziona in questo modo:
- SafeEvent è una procedura che permette di eseguire un RaiseEvent in modo thread safe
- Connect imposta una variabile per indicare lo stato del socket su connecting poi usa il metodo del TcpClient BeginConnect con callback ad una sub che mette lo status su connected, prende il Networkstream, avvia il thread per controllare lo stato del socket ed esegue un SafeEvent sull'evento Connected
- Listen imposta lo status su listening e avvia un nuovo thread contenente un ciclo che si ferma solo quando il TcpListener riceve una richiesta di connessione, esegue un SafeEvent sull'evento ConnectionRequest e se l'user non ha deciso di rifiutare la connessione accetta il socket,imposta il tcpClient, prende il Networkstream,avvia il thread per controllare lo stato del socket infine fà un SafeEvent sul'evento Connected
- TCheck (il thread per controllare lo stato del socket),controlla se il socket è ancora connesso (come alcuni di voi sapranno la proprietà .connected si riferisce solamente all'ultima operazione di I\O eseguita,di conseguenza ho dato un'occhiata a msdn.microsoft.com/it-it/library/…) se lo è avvia il TData sennò chiama Close() e poi fà il SafeEvent di ConnectionLost
- TData controlla se ci sono dati in arrivo,se ce ne sono solleva in modo safe DataArrival sennò esegue TCheck
- Close() in teoria dovrebbe chiudere tutto ma penso sia proprio questo che mi dà problemi


Questa è la classe (ho inserito dei region per facilitare la lettura,quindi magari conviene usare Visual Studio direttamente, conviene quotare il messaggio e prendere il codice dall'editor):
Class MikleSocket 'by VirtualMikle
#Region "Declarations and State property"
    'sockets tcp/ip
    Private tcpClient As Net.Sockets.TcpClient, tcpListener As Net.Sockets.TcpListener, NetStream As Net.Sockets.NetworkStream
    'multi-threading listening, thread for new data ,thread for disconnection checking
    Private TListen As Threading.Thread, TData As Threading.Thread, TCheck As Threading.Thread
    'used to close an output connection request, socket status
    Private ConnectAsync As IAsyncResult, _Condition As Status = Status.Disconnected
    'events
    Public Event Connected As EventHandler
    Public Event ConnectionLost As Eventhandler
    Public Event ConnectionRequest As System.ComponentModel.CancelEventHandler
    Public Event DataArrival(ByVal Data() As Byte)

    'socket's statuses
    Public Enum Status
        Connected
        Disconnected
        ConnectionRequest
        Listening
        Connecting
    End Enum

    'return the current socket's status
    Public ReadOnly Property Condition As Status
        Get
            Return _Condition
        End Get
    End Property
#End Region

#Region "Connect"
    Public Sub BeginConnect(ByVal Host As String, ByVal Port As UInt32)
        tcpClient = New Net.Sockets.TcpClient
        'start the operation on another thread and save the IAsyncResult so we'll be able to kill this request
        _Condition = Status.Connecting
        ConnectAsync = tcpClient.BeginConnect(Host, Port, AddressOf ThreadConnected, Nothing)
    End Sub

    Private Sub ThreadConnected()
        'get packets stream
        NetStream = tcpClient.GetStream
        'set the status and destroy the  ConnectAsync 
        _Condition = Status.Connected
        ConnectAsync = Nothing
        'start checking status
        TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
        SafeEvent(ConnectedEvent, {Me, New EventArgs})
    End Sub
#End Region

#Region "Listen"
    Public Sub BeginListen(ByVal Port As UInt32)
            TListen = New Threading.Thread(AddressOf ThreadListen)
            'get the local ip and set the listener to the specified port
            tcpListener = New Net.Sockets.TcpListener(Net.IPAddress.Parse("127.0.0.1"), Port)
            'start listening and set the socket status
        tcpListener.Start()
            _Condition = Status.Listening
            'start the aynchronous operation
            TListen.Start()
    End Sub

    'asynchronous operation
    Private Sub ThreadListen()
        Dim Cancel As New System.ComponentModel.CancelEventArgs
        'just wait until there's a connection request
        Do Until tcpListener.Pending
        Loop
        'raise the connection request event in a thread-safe way
        SafeEvent(ConnectionRequestEvent, {Me, Cancel})
        'check if the connection has not been rejected
        If Not Cancel.Cancel Then
            'accept the connection and get objects
            tcpClient = tcpListener.AcceptTcpClient()
            tcpListener.Stop()
            NetStream = tcpClient.GetStream
            _Condition = Status.Connected
            'start checking status
            TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
            'raise safely the connection completed event
            SafeEvent(ConnectedEvent, {Me, New EventArgs})
        End If
    End Sub
#End Region

#Region "Data and CheckStatus"
    Private Sub GetData()
        'check if there's new data
        If tcpClient.Available > 0 Then
            Dim Buffer(tcpClient.Available - 1) As Byte
            'get the new data
            NetStream.Read(Buffer, 0, Buffer.Length)
            'thread safe dataarrival event
            SafeEvent(DataArrivalEvent, {Buffer})
        End If
        'start a thread to see if the socket is still connected
        TCheck = New Threading.Thread(AddressOf CheckStatus) : TCheck.Start()
    End Sub

    'write the byte data (send a packet)
    Public Sub SendData(ByVal Data() As Byte)
        'if it not connected then throw the right exception
        If _Condition = Status.Connected Then
            NetStream.Write(Data, 0, Data.Length)
        Else : Throw New Net.Sockets.SocketException(10057)
        End If
    End Sub

    'overloads with string data
    Public Sub SendData(ByVal Data As String)
        If _Condition = Status.Connected Then
            Dim Buffer() As Byte = System.Text.UTF8Encoding.UTF8.GetBytes(Data)
            NetStream.Write(Buffer, 0, Buffer.Length)
        Else : Throw New Net.Sockets.SocketException(10057)
        End If
    End Sub

    'check if the socket is still connected
    Private Sub CheckStatus()
            If tcpClient.Available = 0 AndAlso tcpClient.Client.Poll(0, Net.Sockets.SelectMode.SelectRead) AndAlso tcpListener Is Nothing Then
                'close all apart from this thread
                Close()
                'thread safe connectionlost event
            SafeEvent(ConnectionLostEvent, {Me, New EventArgs})
            'close this thread
            Exit Sub
            End If
            'begin checking for new data
            TData = New Threading.Thread(AddressOf GetData) : TData.Start()
    End Sub
#End Region

#Region "Close and SafeEvent"
    'close all
    Public Sub Close()
        On Error Resume Next
        'abort listening thread
        If TListen IsNot Nothing Then TListen.Abort()
        'end tcpclient connection request
        If ConnectAsync IsNot Nothing Then
            tcpClient.EndConnect(ConnectAsync)
            ConnectAsync = Nothing
        End If
        'abort checking-data thread
        If TData IsNot Nothing Then TData.Abort()
        'close listener
        If tcpListener IsNot Nothing Then tcpListener.Server.Close()
        'close client
        If tcpClient IsNot Nothing Then tcpClient.Close()
        'close networkstream
        If NetStream IsNot Nothing Then NetStream.Close()
        _Condition = Status.Disconnected
    End Sub

    'thread safe raiseevent
    Private Sub SafeEvent(ByVal MyEvent As [Delegate], ByVal Parameters() As Object)
        If MyEvent IsNot Nothing Then
            Dim CurObj As Object
            'get all the delegates
            For Each MyDel As [Delegate] In MyEvent.GetInvocationList
                CurObj = MyDel.Target
                CurObj.BeginInvoke(MyDel, Parameters)
            Next
        End If
    End Sub
#End Region
End Class




Questo è il mini-progettino che ho fatto in una windows form per testare (richieste una listbox ListBox1 e due buttons Button1 e Button2,scusate se non li ho rinominati ma era solo per testare xD)
WithEvents client, server As New MikleSocket
    'usata per non fare uno scambio di pacchetti infinito (vedi client_DataArrival)
    Private yet As Boolean = True

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        server.BeginListen(200)
        client.BeginConnect("localhost", 200)
    End Sub

    Private Sub server_Connected(ByVal sender As Object, ByVal e As EventArgs) Handles server.Connected
        Do Until client.Condition = MikleSocket.Status.Connected
            Application.DoEvents()
        Loop
        server.SendData("server->client: who are you?")
    End Sub

    Private Sub client_DataArrival(ByVal Data() As Byte) Handles client.DataArrival
        ListBox1.Items.Add(System.Text.UTF8Encoding.UTF8.GetString(Data))
        If yet Then
            yet = False
            client.SendData("client-server: i'm your client")
        End If
    End Sub

    Private Sub Server_DataArrival(ByVal Data() As Byte) Handles server.DataArrival
        ListBox1.Items.Add(System.Text.UTF8Encoding.UTF8.GetString(Data))
        server.SendData("server->client: nice to meet you!")
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        server.Close()
    End Sub

    Private Sub server_ConnectionLost(ByVal sender As Object, ByVal e As EventArgs) Handles client.ConnectionLost
        ListBox1.Items.Add("client has lost the connection")
        yet = True
    End Sub

    Private Sub client_ConnectionLost(ByVal sender As Object, ByVal e As EventArgs) Handles server.ConnectionLost
        ListBox1.Items.Add("server has lost the connection")
    End Sub
End Class
Ultima modifica effettuata da Question 05/02/11 14:48
aaa