Lycaon
asked on
Seeking complete asynchronous, thread-safe TCP/UDP socket implementation for VB.NET
I am currently migrating to VB.NET, and have converted all of my VB6 projects to VB.NET, however, I would like to get away from the dependency on the old winsock library and go to full framework dependency. My problem is, the Sockets namespace in general is driving me nuts! Instead of creating an array of Winsock controls and dealing with them, I now have to consider thread safety, etc.
I have spent the last 18 hours scouring the net for either instructions, or an actual class that mimics the Winsock control as closely as possible, and have found none that encompass both thread-safety and asynchronous callbacks.
The info / class I'm looking for needs to include:
Proper use of creating a thread-safe method of creating an asynchronous 'listen' and 'connect' connection, including data retrieval/closing
Proper implementation of a thread-safe Event for both of these.
As an example, I am porting a VB6 game server to VB.NET, and need the server to be able to handle multiple connections as it did in VB6, by having one Winsock control listen for connections, then handing off the RequestID to a separate array of Winsock controls.
I have not had time to experiment with UDP yet, but I'm assuming it's similar to TCP, but I'd like information on doing this asynchronously as well.
I will increase points for more detailed information, up to 500 for a complete class. It'd be worth a lot more to me, but unfortunately I've noticed that a cap has been placed on points awarded. Please note that, in the interest of rewarding the person who has helped me out the most, I'd like to refrain from splitting points, so if it's just a small code snippet here or there, or a URL to a site that doesn't provide more than what I am looking for, I'll avoid considering the comment as an answer. Sorry guys, but limiting the points a guy can reward limits the people he can reward. :\
I have spent the last 18 hours scouring the net for either instructions, or an actual class that mimics the Winsock control as closely as possible, and have found none that encompass both thread-safety and asynchronous callbacks.
The info / class I'm looking for needs to include:
Proper use of creating a thread-safe method of creating an asynchronous 'listen' and 'connect' connection, including data retrieval/closing
Proper implementation of a thread-safe Event for both of these.
As an example, I am porting a VB6 game server to VB.NET, and need the server to be able to handle multiple connections as it did in VB6, by having one Winsock control listen for connections, then handing off the RequestID to a separate array of Winsock controls.
I have not had time to experiment with UDP yet, but I'm assuming it's similar to TCP, but I'd like information on doing this asynchronously as well.
I will increase points for more detailed information, up to 500 for a complete class. It'd be worth a lot more to me, but unfortunately I've noticed that a cap has been placed on points awarded. Please note that, in the interest of rewarding the person who has helped me out the most, I'd like to refrain from splitting points, so if it's just a small code snippet here or there, or a URL to a site that doesn't provide more than what I am looking for, I'll avoid considering the comment as an answer. Sorry guys, but limiting the points a guy can reward limits the people he can reward. :\
ASKER
And that's all there is to an all async, thread-safe server and client? None of the server sockets are goingto block the main thread, or the threads of the other sockets?
I have used a code piece quite similar to the one i have put here, and so far i have not faced any problem with it.
ASKER
Alrighty, let me give it a go and I'll accept your answer soon.
ASKER
checco:
I was hoping for a bit more generic / reusable solution. For example, in the OnDataReceived sub in the server class, there is the line:
Sender.Send(strResponseStr ing)
I'd rather this raise an event so that I can a) handle the data in whatever module I declared the Server class in, and b) be able to reuse the server/client classes without having to edit that particular sub each time (which kind of ties in with a)
Feel like revising at all? I'm still willing to increase points for better code.
I was hoping for a bit more generic / reusable solution. For example, in the OnDataReceived sub in the server class, there is the line:
Sender.Send(strResponseStr
I'd rather this raise an event so that I can a) handle the data in whatever module I declared the Server class in, and b) be able to reuse the server/client classes without having to edit that particular sub each time (which kind of ties in with a)
Feel like revising at all? I'm still willing to increase points for better code.
a) handle the data in whatever module I declared the Server class in
you can declare a public event in client and raise that event in the send method. Have a handler for that event in whatever module you declared the server.
b) be able to reuse the server/client classes without having to edit that particular sub each time
for each instance of the server you can write specific code in the event handler (as mentioned in the above point a)
you can declare a public event in client and raise that event in the send method. Have a handler for that event in whatever module you declared the server.
b) be able to reuse the server/client classes without having to edit that particular sub each time
for each instance of the server you can write specific code in the event handler (as mentioned in the above point a)
ASKER
checoo:
Still messing with it, I'm having problems getting the event to work right. I kinda jumped headlong into .NET and it seems I'm in danger of drowning, hehe.
Still messing with it, I'm having problems getting the event to work right. I kinda jumped headlong into .NET and it seems I'm in danger of drowning, hehe.
can you post some code snippets as to where u r having problem
ASKER
Problem is getting the code to raise events properly.
Pretty much I need an event with two arguments, a string GUID to identify the actual connection, and a byte array that holds the received data. Then, in the event, I'd handle some variable and database manipulation, broadcast these changes to some of the other connections, and wait for more data.
Pretty much I need an event with two arguments, a string GUID to identify the actual connection, and a byte array that holds the received data. Then, in the event, I'd handle some variable and database manipulation, broadcast these changes to some of the other connections, and wait for more data.
can you post some code snippets..and from where do you want to raise the event
ASKER
I can't post actual pieces of the working code due to an NDA, but pretty much what I was looking for is a class that behaves much like a Winsock control array.
EG, in the main form I'd Dim SocketArray As ThisSocketClass, then modify it so it would have .CreateSocket (which would return a GUID), Send with GUID as the argument, Close with GUID as the argument, then a Connect, Disconnect, and DataArrival event with the GUID, and in the case of DataArrival, a byte array as arguments.
The event should be raised from inside the class that has the actual array of sockets... But the event would be raised IN the form where the class was dimmed.
I really have looked into this, I'm just working 60+ hour weeks at my normal job, and I've got this on the side to complete. :\
EG, in the main form I'd Dim SocketArray As ThisSocketClass, then modify it so it would have .CreateSocket (which would return a GUID), Send with GUID as the argument, Close with GUID as the argument, then a Connect, Disconnect, and DataArrival event with the GUID, and in the case of DataArrival, a byte array as arguments.
The event should be raised from inside the class that has the actual array of sockets... But the event would be raised IN the form where the class was dimmed.
I really have looked into this, I'm just working 60+ hour weeks at my normal job, and I've got this on the side to complete. :\
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
If lycaon can post some sample code snippets related to his problem, I am still ready to help..
Server
=====
Imports System.Net
Imports System.Net.Sockets
Imports System.Threading
Imports System.Collections
Public Class SocketServer
Private ClientCol As Hashtable
Private objThread As Thread
Private objListner As TcpListener
Private intListeningPort As Integer
Public Sub New(ByVal intPort As Integer)
ClientCol = New Hashtable
intListeningPort = intPort
objThread = New Thread(AddressOf DoListen)
objThread.Start()
End Sub
Private Sub DoListen()
Dim newClient As Client
Try
objListner = New TcpListener(IPAddress.Any,
objListner.Start()
aaa: Do
newClient = New Client(objListner.AcceptTc
ClientCol.Add(newClient.Cl
AddHandler newClient.Connected, AddressOf onConnected
AddHandler newClient.Disconnected, AddressOf onDisconected
AddHandler newClient.DataReceived, AddressOf onDataReceived
Loop Until False
Catch e As Exception
GoTo aaa
End Try
End Sub
Private Sub onConnected(ByVal Sender As Client)
ClientCol.Add(Sender.Clien
End Sub
Private Sub onDisconected(ByVal Sender As Client)
Sender.DisposeClient()
ClientCol.Remove(Sender.Cl
End Sub
Private Sub onDataReceived(ByVal Sender As Client, ByVal strData As String)
Try
'write code to process the inputs
Sender.Send(strResponseStr
Catch e As Exception
Sender.Send(e.Message.ToSt
End Try
End Sub
Public Sub DisposeServer()
'clean up code
End Sub
End Class
Client
====
Imports System.Net.Sockets
Imports System.Text
Public Class Client
Public Event Connected(ByVal sender As Client)
Public Event Disconnected(ByVal sender As Client)
Public Event DataReceived(ByVal sender As Client, ByVal strData As String)
Private myID As Guid
Private objClient As TcpClient
Private bClientData(1024) As Byte
Private objText As New StringBuilder
Public Sub New(ByVal Client As TcpClient, ByVal strConn As String)
Dim strResponse As String
objClient = Client
objClient.NoDelay = True
myID = Guid.NewGuid
End Sub
Public ReadOnly Property ClientID() As String
Get
Return myID.ToString
End Get
End Property
Private Sub ReceiveClientData(ByVal asyncResult As IAsyncResult)
Dim intcount As Integer
Try
SyncLock objClient.GetStream
intcount = objClient.GetStream.EndRea
End SyncLock
If intcount < 1 Then
RaiseEvent Disconnected(Me)
Exit Sub
End If
ByteToString(bClientData, intcount)
SyncLock objClient.GetStream
objClient.GetStream.BeginR
End SyncLock
Catch e As Exception
RaiseEvent Disconnected(Me)
End Try
End Sub
Private Sub ByteToString(ByVal Bytes() As Byte, ByVal intCount As Integer)
Dim intIndex As Integer
For intIndex = 0 To intCount - 1
If Bytes(intIndex) = 94 Then
RaiseEvent DataReceived(Me, objText.ToString)
objText = New StringBuilder
Else
objText.Append(ChrW(Bytes(
End If
Next
End Sub
Public Sub Send(ByVal strData As String)
SyncLock objClient.GetStream
Dim wri As New IO.StreamWriter(objClient.
wri.Write(strData)
wri.Flush()
End SyncLock
If blnOracleError Then
blnOracleError = False
objconn = Nothing
RaiseEvent Disconnected(Me)
End If
End Sub
Public Sub Dispose()
'Clean up code here
End Sub
End Class