Solved

Seeking complete asynchronous, thread-safe TCP/UDP socket implementation for VB.NET

Posted on 2004-10-04
16
1,304 Views
Last Modified: 2008-03-17
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. :\
0
Comment
Question by:Lycaon
  • 7
  • 6
16 Comments
 
LVL 9

Expert Comment

by:checoo
ID: 12217012
To start with you need to use the TCPListen and TCPClient, and to make it thread safe you need to use a Synclock. Start the listening process on a seperate process, look at the samples below --

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, intListeningPort)
            objListner.Start()
aaa:        Do
                newClient = New Client(objListner.AcceptTcpClient, strConnectionString)
                ClientCol.Add(newClient.ClientID, newClient)
                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.ClientID, Sender)
    End Sub

    Private Sub onDisconected(ByVal Sender As Client)
        Sender.DisposeClient()
        ClientCol.Remove(Sender.ClientID)
    End Sub

    Private Sub onDataReceived(ByVal Sender As Client, ByVal strData As String)
        Try
            'write code to process the inputs
            Sender.Send(strResponseString)
        Catch e As Exception
            Sender.Send(e.Message.ToString)
        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.EndRead(asyncResult)
            End SyncLock

            If intcount < 1 Then
                RaiseEvent Disconnected(Me)
                Exit Sub
            End If

            ByteToString(bClientData, intcount)

            SyncLock objClient.GetStream
                objClient.GetStream.BeginRead(bClientData, 0, 1024, AddressOf ReceiveClientData, Nothing)
            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(intIndex)))
            End If
        Next
    End Sub

    Public Sub Send(ByVal strData As String)
        SyncLock objClient.GetStream
            Dim wri As New IO.StreamWriter(objClient.GetStream)
            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
0
 
LVL 1

Author Comment

by:Lycaon
ID: 12219233
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?
0
 
LVL 9

Expert Comment

by:checoo
ID: 12223964
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.
0
 
LVL 1

Author Comment

by:Lycaon
ID: 12225779
Alrighty, let me give it a go and I'll accept your answer soon.
0
 
LVL 1

Author Comment

by:Lycaon
ID: 12236520
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(strResponseString)

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.
0
 
LVL 9

Expert Comment

by:checoo
ID: 12237938
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)
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 1

Author Comment

by:Lycaon
ID: 12437546
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.
0
 
LVL 9

Expert Comment

by:checoo
ID: 12455944
can you post some code snippets as to where u r having problem
0
 
LVL 1

Author Comment

by:Lycaon
ID: 12622461
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.
0
 
LVL 9

Expert Comment

by:checoo
ID: 12638018
can you post some code snippets..and from where do you want to raise the event
0
 
LVL 1

Author Comment

by:Lycaon
ID: 12652095
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. :\
0
 
LVL 9

Accepted Solution

by:
checoo earned 175 total points
ID: 12652340
instead of Sender.Send(strResponseString) you can raise an event, and subscribe to that event where ever you want to handle it.
0
 
LVL 9

Expert Comment

by:checoo
ID: 12864338
If lycaon can post some sample code snippets related to his problem, I am still ready to help..
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Creating an analog clock UserControl seems fairly straight forward.  It is, after all, essentially just a circle with several lines in it!  Two common approaches for rendering an analog clock typically involve either manually calculating points with…
The ECB site provides FX rates for major currencies since its inception in 1999 in the form of an XML feed. The files have the following format (reducted for brevity) (CODE) There are three files available HERE (http://www.ecb.europa.eu/stats/exch…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

26 Experts available now in Live!

Get 1:1 Help Now