VB.NET 2005 - Sockets - TcpClient - Custom Event To Read Data

Hi Experts,
I found the following code that does the job of a winsock control. But this is a sub that you may call by a click of a button, and what I need is an even that gets fired whenever the socket reads data. Something like dataArrival in vb6 winsock!

How may I ammend this code to do that job?


Thanks!
Sub Test()
 
        Dim tcpClient As New System.Net.Sockets.TcpClient()
        tcpClient.Connect("192.0.3.18", 5555)
        Dim networkStream As NetworkStream = tcpClient.GetStream()
        If networkStream.CanWrite And networkStream.CanRead Then
            '' Do a simple write.
            'Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes("Is anybody there")
            'networkStream.Write(sendBytes, 0, sendBytes.Length)
 
            ' Read the NetworkStream into a byte buffer.
            Dim bytes(tcpClient.ReceiveBufferSize) As Byte
            networkStream.Read(bytes, 0, CInt(tcpClient.ReceiveBufferSize))
 
            ' Output the data received from the host to the console.
            Dim returnData As String = Encoding.ASCII.GetString(bytes)
            'Console.WriteLine(("Host returned: " + returndata))
            Print(returnData)
        Else
            If Not networkStream.CanRead Then
                Print("cannot not write data to this stream")
                tcpClient.Close()
            Else
                If Not networkStream.CanWrite Then
                    Print("cannot read data from this stream")
                    tcpClient.Close()
                End If
            End If
        End If
        ' pause so user can view the console output
        'Console.ReadLine()
    End Sub

Open in new window

feesuAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

AUmidhCommented:
feesu!

you will have to read it by starting a thread.....
add a method ReadClient()
Dim rThread as Thread

rThread=New Thread(addressof ReadClient)
rThread.Start()

' and do all your reading here... and if you want to notify it to some other class then raise some events from here.
Sub ReadClient()

            Dim bytes(tcpClient.ReceiveBufferSize) As Byte
            networkStream.Read(bytes, 0, CInt(tcpClient.ReceiveBufferSize))
 
            ' Output the data received from the host to the console.
            Dim returnData As String = Encoding.ASCII.GetString(bytes)
            'Console.WriteLine(("Host returned: " + returndata))
            Print(returnData)
End Sub
0
feesuAuthor Commented:
AUmidh,

Is there a difference between a Thread and a Delegate?
0
AUmidhCommented:
Yes!

I think there is no comparason of a thread and a delegate both are two different things.

a thread mean to execute another process within the running process.

where a delegate is just a point to a function.
0
Become a CompTIA Certified Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

feesuAuthor Commented:
But I was reading through examples on my problem trying to fix it. And I noticed that someone has faced the same issue, when he was recommended to use delegates.

I had never used delegates before, but looked at that example and tried the same and stopped getting that error that used to say that this UI is being updated by another thread!
0
AUmidhCommented:
can you kindly display your all code....
what are you doing.... I think you can read in a thread and when you read data you can call to a delegate... display your code.
0
AUmidhCommented:
>> that error that used to say that this UI is being updated by another thread!

you did not mention any error before your last comment.
0
feesuAuthor Commented:
You are right, I did not mentino, because there was a different issue. I used another socket custom class that already has an event.

Then, when I ran the project, I started getting the Threading error. And that's when I asked you.

If you wish to help me in my problem, I shall be greatful!

My scenario is that I used to use winsock in vb6 to read market prices on spot. Now, in .net I noticed that winsock doesn't perform properly, and gives "stack overflow" and "threading" errors.

I was recommended to use .net socket classes.

In my code, I use an MDI to read the data feed and accordingly send it to the relevant child. But there are other issues that I started facing; I write to xml file in one of the feed cases and read from it, and this is causing a delay, I think I am not doing it properly.

Another issue is that I notice delay in using the datatable.select  because I use it alot. The datafeed I receive is really huge and fast.

Please find attached my code:
Imports System.Windows.Forms
Imports System.IO
 
Public Class MDI
 
    Dim AR
    Dim dTime As String, dDate As String
    Dim BidQTY, AskQty As Double
    Dim I As Integer
 
    Public bMarketClosed As Boolean, bConnectionError As Boolean, iReconnectingTimes As Integer
 
    Dim dtQuotes_Master As New DataTable
 
    Public dsQuotes_Master As New DataSet
    Public sXML_Quotes_Master As String = Application.StartupPath & "\quotes_master.xml"
    Public bXML_Quotes_Master_Exists As Boolean
 
    Private WithEvents wsClient As New SocketsClient()
 
    Delegate Sub UpdateTextHandler(ByVal message As String)
    Public Sub UpdateText(ByVal message As String)
        Me.Text = message
    End Sub
 
    Delegate Sub OnFly_TS_DataArrival_Delegate(ByVal AR)
    Public Sub OnFly_TS_DataArrival(ByVal AR)
        OnFly_TS.DataArrival(AR)
    End Sub
 
    Delegate Sub OnFly_Q_DataArrival_Delegate(ByVal AR)
    Public Sub OnFly_Q_DataArrival(ByVal AR)
        OnFly_Q.DataArrival(AR)
    End Sub
 
    Delegate Sub OnFly_OB_DataArrival_Delegate(ByVal AR)
    Public Sub OnFly_OB_DataArrival(ByVal AR)
        OnFly_OB.DataArrival(AR)
    End Sub
 
 
    Private Sub Default_OnFly_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 
        With dtQuotes_Master
            .Columns.Add(New DataColumn("Row", GetType(System.Double)))
            .Columns.Add(New DataColumn("StockID", GetType(System.Double)))
            .Columns.Add(New DataColumn("Stock_E", GetType(System.String)))
            .Columns.Add(New DataColumn("Stock_A", GetType(System.String)))
        End With
 
        If File.Exists(sXML_Quotes_Master) Then
            bXML_Quotes_Master_Exists = True
            dsQuotes_Master.ReadXml(sXML_Quotes_Master)
        Else
            bXML_Quotes_Master_Exists = False
            dsQuotes_Master.Tables.Add(dtQuotes_Master)
        End If
 
        With OnFly_TS
            .MdiParent = Me
            .Show()
        End With
 
        With OnFly_Q
            .MdiParent = Me
            .Show()
        End With
 
        With OnFly_OB
            .MdiParent = Me
            .Show()
        End With
 
    End Sub
 
    Sub EJ_QuotesMaster_Refresh()
        If File.Exists(sXML_Quotes_Master) Then
            dsQuotes_Master.ReadXml(sXML_Quotes_Master)
        End If
    End Sub
 
    Private Sub tmrFeed_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrFeed.Tick
        iReconnectingTimes = iReconnectingTimes + 1
        ReConnect(True, CStr(iReconnectingTimes))
    End Sub
 
    Sub ReConnect(ByVal bReconnecting As Boolean, Optional ByVal iCount As String = "")
 
        Dim sText As String
        If bReconnecting = True Then
            sText = "Reconnecting"
            Me.Winsock1.Close()
        Else
            sText = "Connecting"
        End If
 
        Me.Text = vbCrLf & _
                          "------------------------------------------------------------------------------" & _
                          vbCrLf & _
                          sText & " @ " & Now & _
                          vbCrLf & _
                          "------------------------------------------------------------------------------" & _
                          vbCrLf & _
                          Me.Text
 
        wsClient.Connect("192.0.3.18", 5555)
 
    End Sub
 
    Private Sub wsClient_onConnect() Handles wsClient.onConnect
        Me.tmrFeed.Enabled = False
        iReconnectingTimes = 0
        bConnectionError = False
    End Sub
 
    Private Sub wsClient_onDisconnect() Handles wsClient.onDisconnect
        Me.tmrFeed.Enabled = True
    End Sub
 
    Private Sub wsClient_onDataArrival(ByVal Data() As Byte, ByVal TotalBytes As Integer) Handles wsClient.onDataArrival
        Try
            Dim sText As String = wsClient.BytestoString(Data)
 
            Dim arCR
 
            arCR = Split(sText, vbCrLf)
            If Not (Trim(sText) = "" Or UBound(arCR) = 1) Then
 
                For I = 0 To UBound(arCR) - 1
 
                    If (Trim(arCR(0)) <> "") And (Len(arCR(0)) > 1) Then
 
                        arCR(I) = Trim(Mid(Trim(arCR(I)), 1, Len(Trim(arCR(I))) - 2))
                        Dim displayText = IIf(bMarketClosed, "Market Closed", "Market Open") & "   -   " & arCR(I)
                        Me.Invoke(New UpdateTextHandler(AddressOf UpdateText), New Object() {displayText})
 
 
                        AR = Split(arCR(I), "|")
                        If UBound(AR) > 0 Then
 
                            Select Case AR(0)
 
                                Case "$MM"
                                    If AR(3) = 4 Then
                                        bMarketClosed = False
                                    Else
                                        bMarketClosed = True
                                    End If
 
                                Case "$CH"
                                    If AR(11) = 4 Then
                                        bMarketClosed = False
                                    Else
                                        bMarketClosed = True
                                    End If
                                Case "$LL"
                                    'If dsQuotes_Master.Tables.Count = 1 Then ' to make sure it's not added more than 1 table
                                    'If File.Exists(Me.sXML_Quotes_Master) Then
                                    'If bXML_Quotes_Master_Exists Then
                                    '>>>> Why do you need to check?? You have already added a table to the dataset!
                                    Dim arRows() As DataRow = dsQuotes_Master.Tables(0).Select("ROW=" & Val(AR(2)))
                                    If arRows.Length <> 0 Then dsQuotes_Master.Tables(0).Rows.Remove(arRows(0))
                                    'End If
 
                                    Dim Row1 As DataRow
 
                                    'Row1 = dtQuotes_Master.NewRow()
                                    Row1 = dsQuotes_Master.Tables(0).NewRow
 
                                    Row1("Row") = AR(2)
                                    Row1("StockID") = AR(3)
                                    Row1("Stock_E") = AR(4)
                                    Row1("Stock_A") = AR(5)
 
                                    'dtQuotes_Master.Rows.Add(Row1)
                                    dsQuotes_Master.Tables(0).Rows.Add(Row1)
 
                                    'dsQuotes_Master.Tables.Add(dtQuotes_Master)
                                    File.Delete(Me.sXML_Quotes_Master)
                                    dsQuotes_Master.WriteXml(Me.sXML_Quotes_Master) '>>> You should not write the file on every hit PLUS need to delet existing as well
                                    'dsQuotes_Master.Tables.Remove(dtQuotes_Master)
 
                                Case "$TT"
                                    'Application.DoEvents()
                                    'OnFly_TS.DataArrival(AR)
                                    Me.Invoke(New OnFly_TS_DataArrival_Delegate(AddressOf OnFly_TS_DataArrival), New Object() {AR})
                                Case "$QQ"
                                    'Application.DoEvents()
                                    'OnFly_Q.DataArrival(AR)
                                    Me.Invoke(New OnFly_Q_DataArrival_Delegate(AddressOf OnFly_Q_DataArrival), New Object() {AR})
                                Case "$XX"
                                    'Application.DoEvents()
                                    'OnFly_Q.DataArrival(AR)
                                    Me.Invoke(New OnFly_Q_DataArrival_Delegate(AddressOf OnFly_Q_DataArrival), New Object() {AR})
                                Case "$KB"
                                    If bMarketClosed Then Exit Select
                                    'OnFly_OB.DataArrival(AR)
                                    Me.Invoke(New OnFly_OB_DataArrival_Delegate(AddressOf OnFly_OB_DataArrival), New Object() {AR})
                                Case "$KS"
                                    If bMarketClosed Then Exit Select
                                    'OnFly_OB.DataArrival(AR)
                                    Me.Invoke(New OnFly_OB_DataArrival_Delegate(AddressOf OnFly_OB_DataArrival), New Object() {AR})
                            End Select
                        End If
                    End If
                Next
            End If
        Catch ex As Exception
            Text = ex.Message
        End Try
 
    End Sub
 
End Class

Open in new window

0
AUmidhCommented:
----feesu:---------

I think here you are writing to xml....

'If dsQuotes_Master.Tables.Count = 1 Then ' to make sure it's not added more than 1 table
                                    'If File.Exists(Me.sXML_Quotes_Master) Then
                                    'If bXML_Quotes_Master_Exists Then
                                    '>>>> Why do you need to check?? You have already added a table to the dataset!
                                    Dim arRows() As DataRow = dsQuotes_Master.Tables(0).Select("ROW=" & Val(AR(2)))
                                    If arRows.Length <> 0 Then dsQuotes_Master.Tables(0).Rows.Remove(arRows(0))
                                    'End If
 
                                    Dim Row1 As DataRow
 
                                    'Row1 = dtQuotes_Master.NewRow()
                                    Row1 = dsQuotes_Master.Tables(0).NewRow
 
                                    Row1("Row") = AR(2)
                                    Row1("StockID") = AR(3)
                                    Row1("Stock_E") = AR(4)
                                    Row1("Stock_A") = AR(5)
 
                                    'dtQuotes_Master.Rows.Add(Row1)
                                    dsQuotes_Master.Tables(0).Rows.Add(Row1)
 
                                    'dsQuotes_Master.Tables.Add(dtQuotes_Master)
                                    File.Delete(Me.sXML_Quotes_Master)
                                    dsQuotes_Master.WriteXml(Me.sXML_Quotes_Master) '>>> You should not write the file on every hit PLUS need to delet existing as well
                                    'dsQuotes_Master.Tables.Remove(dtQuotes_Master)

if yes. why you are doing like this.. I think
you can load your xml file at application start write another node in it and save it at the end or at every turn..just study xmlNode,xmlElement and xmlDocument class... and don't use datatable and dataset for that.

0
feesuAuthor Commented:
You mean this is slowing up the process? But I am making use of the datatable later in my code.
0
AUmidhCommented:
why you are using datatable in this program if for xml operation then don't do like this... use xml instead send me your sample xml and what you want to do in that describe. ...
0
feesuAuthor Commented:
I am not using a database at all. I receive datafeed from an internal server via tcp/ip and I read it on fly then add it into relevant datatables in order to display in grids!

That's why I want to do.

My concern is to achieve grids that display prices with no delay.

In addition, Please send me a recommended sample code for maintaining the xml file I have!


Thanks!
0
AUmidhCommented:
OK i will send you tomorrow bcs this time i am leaving my development PC.
0
feesuAuthor Commented:
Thanks alot for your time AUmidh!
0
AUmidhCommented:
' Please read out all comments bcs this is actual code which is commented and instruction for you.

''''' XML FILE
<?xml version="1.0" ?>
- <catalog>
- <book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications with XML.</description>
</book>
- <book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</description>
</book>
- <book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology society in England, the young survivors lay the foundation for a new society.</description>
</book>
</catalog>

'''''' Operations code.
Dim str1 As String = "C:\Books.xml"
        Dim document As New XmlDocument()
        document.Load(str1)
        Dim Rootnode As XmlNode = document.DocumentElement()
        ' Read All Nodes.
        For Each nd As XmlNode In Rootnode.ChildNodes
            ' Getting Attributes .... nd.Attributes
            ' MessageBox.Show(nd.Attributes("id").Value)
            ' getting Child Nodes [Book]
            'For Each cNd as XmlNode in nd.ChildNodes
            'MessageBox.Show(cNd.InnerText)
            'Next
            ' Here you can also Update a specifice Node, or thier Child Node.
        Next
        ' Read Specific Node
        Dim sNode As XmlNode = Rootnode.SelectSingleNode("book[author='Ralls, Kim']")
        ' By Doing this if node exist you will find it and update/Retrieve what ever you want.
        ' MessageBox.Show(sNode.Attributes("id").Value)

        ' Update Id of specific Node.
        sNode.Attributes("id").Value = "New id"
        sNode.ChildNodes(2).InnerText = "Some Text"
        ' Update its child...
        ' Then Save it at this moment or at last of all operations.

        ' Delete Specific Node.
        Dim dNode As XmlNode = Rootnode.SelectSingleNode("book[author='Corets, Eva']")
        Rootnode.RemoveChild(dNode)
        ' Add Node
        ' The best way to add a new node is to clone any node update it and then append it like.
        Dim aNode As XmlNode = Rootnode.ChildNodes(0).CloneNode(True)
        aNode.Attributes("id").Value = "new inserted"
        ' Update its child and then append .
        Rootnode.AppendChild(aNode)

        ' and the end save it to disk.
        document.Save(str1)
0
feesuAuthor Commented:
But why do you recommend using this code instead of the dataset.readXml or writeXml?
0
feesuAuthor Commented:
Hi AUmidh,
One more question on the xml topic, I am trying to implement this code. I got an error cuz the file does not exist. How do i handle that? Other than file.exists() do I need to write a blank structured file first? If yes, can you help me with the code?
0
AUmidhCommented:
feesu!
To run that sample just copy the xml portion code of my last comment and make a file with the name Books.xml and they try the solution .
for your own solution to the problems... you can create a file first.. or you can also create a file through code. with the following code.

Dim dom as New XmlDocument()
dom.LoadXml("<?xml version="1.0" ?><rootNode></rootNode>")
dom.Save("C:\FileName.xml")
0
feesuAuthor Commented:
Thanks for the update, but you didn't answer my previous question, what was the reason behind recommending using this code for xml instead of the dataset.read .write?
0
AUmidhCommented:
I told you bcs  understand from your this code that you are writing xml/ updating your xml through table.. by calling table.WriteXML and delete the existing.... your code given below.

'If File.Exists(Me.sXML_Quotes_Master) Then
                                    'If bXML_Quotes_Master_Exists Then
                                    '>>>> Why do you need to check?? You have already added a table to the dataset!
                                    Dim arRows() As DataRow = dsQuotes_Master.Tables(0).Select("ROW=" & Val(AR(2)))
                                    If arRows.Length <> 0 Then dsQuotes_Master.Tables(0).Rows.Remove(arRows(0))
                                    'End If
 
                                    Dim Row1 As DataRow
 
                                    'Row1 = dtQuotes_Master.NewRow()
                                    Row1 = dsQuotes_Master.Tables(0).NewRow
 
                                    Row1("Row") = AR(2)
                                    Row1("StockID") = AR(3)
                                    Row1("Stock_E") = AR(4)
                                    Row1("Stock_A") = AR(5)
 
                                    'dtQuotes_Master.Rows.Add(Row1)
                                    dsQuotes_Master.Tables(0).Rows.Add(Row1)
 
                                    'dsQuotes_Master.Tables.Add(dtQuotes_Master)
                                    File.Delete(Me.sXML_Quotes_Master)
                                    dsQuotes_Master.WriteXml(Me.sXML_Quotes_Master) '>>> You should not write the file on every hit PLUS need to delet existing as well
                                    'dsQuotes_Master.Tables.Remove(dtQuotes_Master)

--- So that's why i recommend you that just operate on the Load xml in memory and at the closing write it to disk by this way your problem of low processing will be solved. this whole is mine understanding look at your own senario .
0
feesuAuthor Commented:
Hi,

I implemented the below code and got the following error: ex.Message = "Object reference not set to an instance of an object."

I even tried creating the xml file manually by pasting the following into the it:

<?xml version="1.0"?>
<stocks>
<row>0</row>
<stockId>101</stockId>
<stock_a>NBK</stock_a>
<stock_e>NBK</stock_e>
</stocks>

I get this error message on the line in my add function that tries to set the first attribute!
Private Sub Default_OnFly_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
        If File.Exists(Me.sXML_Quotes_Master) Then
            xmlDoc.Load(Me.sXML_Quotes_Master)
            rootNode = xmlDoc.DocumentElement()
        Else
            xmlDoc.LoadXml("<?xml version='1.0' ?><rootNode></rootNode>")
            rootNode = xmlDoc.DocumentElement()
            xmlDoc.Save(Me.sXML_Quotes_Master)
        End If
...
End Sub
 
  Sub EJ_QuotesMaster_Add(ByVal row As Integer, ByVal stockId As Integer, ByVal stock_a As String, ByVal stock_e As String)
        ' Delete Specific Node.
        Dim dNode As XmlNode = rootNode.SelectSingleNode("stock[row=" & row & "]")
        If Not dNode Is Nothing Then rootNode.RemoveChild(dNode)
 
        ' Add Node
        ' The best way to add a new node is to clone any node update it and then append it like.
        Dim aNode As XmlNode = rootNode.ChildNodes(0).CloneNode(True)
        aNode.Attributes("row").Value = row
        aNode.Attributes("stockId").Value = stockId
        aNode.Attributes("stock_a").Value = stock_a
        aNode.Attributes("stock_e").Value = stock_e
 
        ' Update its child and then append .
        rootNode.AppendChild(aNode)
 
        ' and the end save it to disk.
        xmlDoc.Save(Me.sXML_Quotes_Master)
 
    End Sub

Open in new window

0
AUmidhCommented:
you are doing wrong these are not attributes these are childs of <stocks> you can create your file as

<?xml version="1.0"?>
<MainRoot>
      <stocks>
              <row>0</row>
              <stockId>101</stockId>
              <stock_a>NBK</stock_a>
              <stock_e>NBK</stock_e>
      </stocks>
</MainRoot>

Then modify your code as ... Search specific node and then update as following.

Sub EJ_QuotesMaster_Add(ByVal row As Integer, ByVal stockId As Integer, ByVal stock_a As String, ByVal stock_e As String)
        ' Delete Specific Node.
        Dim dNode As XmlNode = rootNode.SelectSingleNode("stock[row=" & row & "]")
        If Not dNode Is Nothing Then
              aNode.ChildeNodes(0).Value = row
              aNode.ChildeNodes(1).Value = stockId
              aNode.ChildeNodes(2).Value = stock_a
             aNode.ChildeNodes(3).Value = stock_e
             xmlDoc.Save(Me.sXML_Quotes_Master)
     End Sub
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
feesuAuthor Commented:
AUmidh,

Implementing this code gives me the following error:

ex.Message = "Cannot set a value on node type 'Element'."

On the line: aNode.ChildNodes(0).Value = row

0
feesuAuthor Commented:
This fixed my problem:

        Dim Node As XmlNode = rootNode.SelectSingleNode("stocks[row=" & row & "]")
        If Not Node Is Nothing Then
            Node.ChildNodes(0).ChildNodes(0).Value = row
            Node.ChildNodes(1).ChildNodes(0).Value = stockId
            Node.ChildNodes(2).ChildNodes(0).Value = stock_a
            Node.ChildNodes(3).ChildNodes(0).Value = stock_e

            rootNode.AppendChild(Node)
        Else
            Dim aNode As XmlNode = rootNode.ChildNodes(0).CloneNode(True)
            aNode.ChildNodes(0).ChildNodes(0).Value = row
            aNode.ChildNodes(1).ChildNodes(0).Value = stockId
            aNode.ChildNodes(2).ChildNodes(0).Value = stock_a
            aNode.ChildNodes(3).ChildNodes(0).Value = stock_e

            rootNode.AppendChild(aNode)
        End If

        xmlDoc.Save(Me.sXML_Quotes_Master)
0
feesuAuthor Commented:
Thanks AUmidh for your time!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.