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
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):
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)
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
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