Imports System Imports System.Collections Imports System.Text Imports System.IO Imports System.Net Imports System.Net.Sockets Imports System.Threading 'Imports System.Windows.Forms Namespace UsingSockets End Namespace 'UsingSockets Class Window1 Public Sub New() ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. ' Create the delegate using the method to update the UI _invokeControl = New InvokeControl(AddressOf InvokeUIThread) CreateURLList() SpinUpSocketPool(socketPool) Dim threads(NUMBER_WORKER_THREADS) As Thread Dim count As Integer For count = 1 To threads.Length - 1 threads(count) = New Thread(New ThreadStart(AddressOf Search)) threads(count).Start() Next count End Sub ' The .NET 2.0 socket enhancements include an asynchronous Connect method and a Disconnect method ' to allow a socket to be re-used without the overhead of creating a new socket. The following example uses these ' new features to create a socket pool to allow worker threads to rapidly search URL's and retrieve ' their home page content. The new features go hand-in-hand as a disconnected socket can only ' be re-used by issuing an asynchronous connect. ' Total number of sockets in the pool Private Shared SOCKET_POOL_SIZE As Integer = 10 ' Total number of worker threads Private Shared NUMBER_WORKER_THREADS As Integer = 3 ' Flag to continue thread run Public Shared runThreads As Boolean = True ' Index into URL list, incremented be each thread Private Shared currentURLCounter As Integer = 0 Private Shared socketPool As New ArrayList(SOCKET_POOL_SIZE) Private Shared urlList As New ArrayList() ' Delegate to allow cross-thread update of UI safely Delegate Sub InvokeControl(ByVal [text] As String) Private Shared _invokeControl As InvokeControl ' Spins up a number of sockets to send data via TCP ' socketPool is the ArrayList to be populated with Socket objects Private Sub SpinUpSocketPool(ByVal socketPool As ArrayList) Dim i As Integer For i = 0 To SOCKET_POOL_SIZE - 1 socketPool.Add(New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) Next i End Sub 'SpinUpSocketPool ' Create a list of URL's by appending a counter to the searchURL ' pointing at a handy list of known sequential URL's Private Sub CreateURLList() urlList.Add("SERVO1") End Sub 'CreateURLList ' Thread event notifier for asynchronous connects Public Shared allDone As New ManualResetEvent(False) ' .NET 2.0 callback that handles the completion of the prior asynchronous ' connect call. The socket is passed contained in the IAsyncResult param via ' the last paramater of Socket.BeginConnect(). ' ar is the state-containing object Public Sub ConnectCallback(ByVal ar As IAsyncResult) allDone.Set() Dim s As Socket = CType(ar.AsyncState, Socket) Try s.EndConnect(ar) Catch End Try End Sub 'ConnectCallback ' Lock the socket pool and find a non-connected socket, then connect it. ' server is The address to connect the socket to ' returns the connected socket Private Function ConnectSocket(ByVal server As String) As Socket Dim s As Socket = Nothing ' Get host related information. UpdateText("Trying " + server) While runThreads SyncLock socketPool Dim i As Integer For i = 0 To SOCKET_POOL_SIZE - 1 s = CType(socketPool(i), Socket) If Not s.Connected Then allDone.Reset() Try s.BeginConnect(server, 10001, New AsyncCallback(AddressOf ConnectCallback), s) Catch End Try ' possible unknown host ' wait here until the connect finishes. The callback ' sets allDone. allDone.WaitOne() Return s End If Next i UpdateText("Out of Sockets!, waiting...") Monitor.Wait(socketPool) UpdateText("Socket added to pool, trying recycled socket") End SyncLock End While Return Nothing End Function 'ConnectSocket '/ Request the home page content for the specified server. '/ server is the address from which to retrieve data Private Sub SocketSendReceive(ByVal server As String) 'Dim request As String = "GET / HTTP/1.1" + ControlChars.Cr + ControlChars.Lf + "Host: " + server + ControlChars.Cr + ControlChars.Lf + "Connection: Close" + ControlChars.Cr + ControlChars.Lf + ControlChars.Cr + ControlChars.Lf Dim request As String = "99ID" & Chr(13) Dim bytesSent As [Byte]() = Encoding.ASCII.GetBytes(request) Dim bytesReceived(10) As [Byte] ' Create a socket connection asynchronously with the specified server and port. Dim s As Socket = ConnectSocket(server) If s Is Nothing Or Not s.Connected Then UpdateText("Failed to connect socket to " + server) Return End If UpdateText("Socket Connected to " + server) ' Send request to the server. s.Send(bytesSent, bytesSent.Length, 0) ' Receive the server home page content. Dim bytes As Integer = 0 Dim page As String = "" ' The following will block until the page contents are retrieved Do bytes = s.Receive(bytesReceived, bytesReceived.Length, 0) page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes) Loop While bytes > 0 ' Use the .NET 2.0 Disconnect method to place the socket back in circulation s.Shutdown(SocketShutdown.Both) s.Disconnect(True) ' boolean true as argument allows socket reuse ' Notify any waiter on the socket pool that a socket has become available SyncLock socketPool Monitor.Pulse(socketPool) End SyncLock UpdateText("Retrieved " + page.Length.ToString() + " bytes from " + server) End Sub 'SocketSendReceive '/ Per-thread method to take next URL from list, incrementing the index into '/ the URL list. Public Sub Search() Dim url As String = Nothing While runThreads SyncLock urlList If currentURLCounter = urlList.Count Then Return End If url = CType(urlList(currentURLCounter), [String]) currentURLCounter = currentURLCounter + 1 End SyncLock SocketSendReceive(url) End While End Sub 'Search ' In order to safely update the UI across threads, a delegate with this method is ' called using Control.Invoke Private Shared Sub InvokeUIThread(ByVal [text] As String) Dim listbox1 As New ListBox listbox1.Items.Add([text]) End Sub 'InvokeUIThread Private Sub UpdateText(ByVal [text] As String) Try 'ListBox1.invoke(_invokeControl, [text]) _invokeControl([text]) Catch End Try End Sub 'UpdateText Private Sub Window1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing runThreads = False End Sub End Class 'Program