EDN Admin
Well-known member
Hello,
Ive got some troubles with the socket multithreading, basically the form closes without returning any exception and if I run my application after the compilation it just gives me an error about a yet disposed object (read the code for more information)
Im quite sure Im mistaking something in the EndReceive method, anyway thats the resource Ive looked into:
<br/>
http://msdn.microsoft.com/en-us/library/w7wtt64b.aspx
I cant figure out why I should use a state object which contains a socket (or tcpclient etc..) variable instead of some public variables in my current socket class.
<pre lang="x-vbnet Imports System.Net.Sockets
Class VMSocket
#Region "Declarations and State property"
sockets tcp/ip
Private tcpClient As Net.Sockets.TcpClient, tcpListener As Net.Sockets.TcpListener
socket status, data buffer
Private _Condition As Status = Status.Disconnected, Buffer(8124) As Byte
events
Public Event Connected As EventHandler
Public Event ConnectionLost As Eventhandler
Public Event DataArrival(ByVal Data() As Byte)
sockets statuses
Public Enum Status
Connected
Disconnected
Listening
Connecting
End Enum
return the current sockets 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
tcpClient.BeginConnect(Host, Port, AddressOf SubConnected, Nothing)
End Sub
Private Sub SubConnected(ByVal ar As IAsyncResult)
close the connection request
tcpClient.EndConnect(ar)
set the status and call the connected event in a thread safe way
_Condition = Status.Connected
SafeEvent(ConnectedEvent, {Me, New EventArgs})
start getting data
tcpClient.Client.BeginReceive(Buffer, 0, Buffer.Length, Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf GetData), Nothing)
End Sub
#End Region
#Region "Listen"
Public Sub BeginListen(ByVal Port As UInt32)
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
tcpListener.BeginAcceptTcpClient(AddressOf ListenComplete, Nothing)
End Sub
asynchronous operation finished
Private Sub ListenComplete(ByVal ar As IAsyncResult)
accept the connection
tcpClient = tcpListener.EndAcceptTcpClient(ar)
stop listening,set the status and raise safely the connected event
tcpListener.Stop()
_Condition = Status.Connected
SafeEvent(ConnectedEvent, {Me, New EventArgs})
start getting data
tcpClient.Client.BeginReceive(Buffer, 0, Buffer.Length, Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf GetData), Nothing)
End Sub
#End Region
#Region "Data"
Private Sub GetData(ByVal ar As IAsyncResult)
check if theres new data
If tcpClient.Client.EndReceive(ar) > 0 Then
SafeEvent(DataArrivalEvent, {Buffer})
Array.Clear(Buffer, 0, Buffer.Length)
tcpClient.Client.BeginReceive(Buffer, 0, Buffer.Length, Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf GetData), Nothing)
if theres not check if the socket has been disconnected (http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.poll.aspx)
ElseIf tcpClient.Client.Poll(0, SelectMode.SelectRead) Then
dispose all the objects and set the status
Close()
_Condition = Status.Disconnected
raise safely an event which warns the user about the disconnection
SafeEvent(ConnectionLostEvent, {Me, New EventArgs})
End If
End Sub
write the byte data (send a packet)
Public Sub SendData(ByVal Data() As Byte)
if the socket is connected then send the data else just throw the right socket exception
If _Condition = Status.Connected Then tcpClient.Client.Send(Data, 0, Data.Length, SocketFlags.None) Else Throw New Net.Sockets.SocketException(10057)
End Sub
overloads with string data
Public Sub SendData(ByVal Data As String)
If _Condition = Status.Connected Then
get bytes from the data string and send them
Dim Buffer() As Byte = System.Text.UTF8Encoding.UTF8.GetBytes(Data)
tcpClient.Client.Send(Buffer, 0, Buffer.Length, SocketFlags.None)
Else : Throw New Net.Sockets.SocketException(10057)
End If
End Sub
#End Region
#Region "Close and SafeEvent"
Public Sub Close()
close listener
If tcpListener IsNot Nothing Then tcpListener.Server.Dispose() : tcpListener = Nothing
close client
If tcpClient IsNot Nothing Then tcpClient.Close()
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[/code]
This is the test code (sorry for the controls names):
<pre lang="x-vbnet Public Class Form1
WithEvents sckClient As New VMSocket, sckServer As New VMTemp
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
sckServer.BeginListen(200)
sckClient.BeginConnect("localhost", 200)
End Sub
Private Sub sckClient_Connected(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles sckClient.Connected
ListBox1.Items.Add("Client connected successfully")
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
sckClient.SendData("Hello")
End Sub
Private Sub sckServer_DataArrival(ByVal Data() As Byte) Handles sckServer.DataArrival
ListBox1.Items.Add(System.Text.Encoding.UTF8.GetString(Data))
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
sckClient.Close()
End Sub
End Class[/code]
View the full article
Ive got some troubles with the socket multithreading, basically the form closes without returning any exception and if I run my application after the compilation it just gives me an error about a yet disposed object (read the code for more information)
Im quite sure Im mistaking something in the EndReceive method, anyway thats the resource Ive looked into:
<br/>
http://msdn.microsoft.com/en-us/library/w7wtt64b.aspx
I cant figure out why I should use a state object which contains a socket (or tcpclient etc..) variable instead of some public variables in my current socket class.
<pre lang="x-vbnet Imports System.Net.Sockets
Class VMSocket
#Region "Declarations and State property"
sockets tcp/ip
Private tcpClient As Net.Sockets.TcpClient, tcpListener As Net.Sockets.TcpListener
socket status, data buffer
Private _Condition As Status = Status.Disconnected, Buffer(8124) As Byte
events
Public Event Connected As EventHandler
Public Event ConnectionLost As Eventhandler
Public Event DataArrival(ByVal Data() As Byte)
sockets statuses
Public Enum Status
Connected
Disconnected
Listening
Connecting
End Enum
return the current sockets 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
tcpClient.BeginConnect(Host, Port, AddressOf SubConnected, Nothing)
End Sub
Private Sub SubConnected(ByVal ar As IAsyncResult)
close the connection request
tcpClient.EndConnect(ar)
set the status and call the connected event in a thread safe way
_Condition = Status.Connected
SafeEvent(ConnectedEvent, {Me, New EventArgs})
start getting data
tcpClient.Client.BeginReceive(Buffer, 0, Buffer.Length, Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf GetData), Nothing)
End Sub
#End Region
#Region "Listen"
Public Sub BeginListen(ByVal Port As UInt32)
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
tcpListener.BeginAcceptTcpClient(AddressOf ListenComplete, Nothing)
End Sub
asynchronous operation finished
Private Sub ListenComplete(ByVal ar As IAsyncResult)
accept the connection
tcpClient = tcpListener.EndAcceptTcpClient(ar)
stop listening,set the status and raise safely the connected event
tcpListener.Stop()
_Condition = Status.Connected
SafeEvent(ConnectedEvent, {Me, New EventArgs})
start getting data
tcpClient.Client.BeginReceive(Buffer, 0, Buffer.Length, Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf GetData), Nothing)
End Sub
#End Region
#Region "Data"
Private Sub GetData(ByVal ar As IAsyncResult)
check if theres new data
If tcpClient.Client.EndReceive(ar) > 0 Then
SafeEvent(DataArrivalEvent, {Buffer})
Array.Clear(Buffer, 0, Buffer.Length)
tcpClient.Client.BeginReceive(Buffer, 0, Buffer.Length, Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf GetData), Nothing)
if theres not check if the socket has been disconnected (http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.poll.aspx)
ElseIf tcpClient.Client.Poll(0, SelectMode.SelectRead) Then
dispose all the objects and set the status
Close()
_Condition = Status.Disconnected
raise safely an event which warns the user about the disconnection
SafeEvent(ConnectionLostEvent, {Me, New EventArgs})
End If
End Sub
write the byte data (send a packet)
Public Sub SendData(ByVal Data() As Byte)
if the socket is connected then send the data else just throw the right socket exception
If _Condition = Status.Connected Then tcpClient.Client.Send(Data, 0, Data.Length, SocketFlags.None) Else Throw New Net.Sockets.SocketException(10057)
End Sub
overloads with string data
Public Sub SendData(ByVal Data As String)
If _Condition = Status.Connected Then
get bytes from the data string and send them
Dim Buffer() As Byte = System.Text.UTF8Encoding.UTF8.GetBytes(Data)
tcpClient.Client.Send(Buffer, 0, Buffer.Length, SocketFlags.None)
Else : Throw New Net.Sockets.SocketException(10057)
End If
End Sub
#End Region
#Region "Close and SafeEvent"
Public Sub Close()
close listener
If tcpListener IsNot Nothing Then tcpListener.Server.Dispose() : tcpListener = Nothing
close client
If tcpClient IsNot Nothing Then tcpClient.Close()
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[/code]
This is the test code (sorry for the controls names):
<pre lang="x-vbnet Public Class Form1
WithEvents sckClient As New VMSocket, sckServer As New VMTemp
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
sckServer.BeginListen(200)
sckClient.BeginConnect("localhost", 200)
End Sub
Private Sub sckClient_Connected(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles sckClient.Connected
ListBox1.Items.Add("Client connected successfully")
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
sckClient.SendData("Hello")
End Sub
Private Sub sckServer_DataArrival(ByVal Data() As Byte) Handles sckServer.DataArrival
ListBox1.Items.Add(System.Text.Encoding.UTF8.GetString(Data))
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
sckClient.Close()
End Sub
End Class[/code]
View the full article