Thursday, April 10, 2008

To use remoting, sockets or web service??

I had to take this decision again for the requirement I have mentioned in my previous post (Therading in .Net). The requirement was to just listen to a port and accept requests and process it. As many of us might do, I started of thinking that remoting is what I should be doing. I did some research comparing remoting with web service and started with writing the code using .Net remoting. As I proceeded further I realised that remoting is not what I wanted. It seemed like a overkill.

I just wanted to listen to a port and accept requests. I do not want to respond back to the client or keep chatting. I can just disconnect if I am able to accept the request properly. I also realised that with remoting, you need to create an instance of the object with the client and the server. This was the turning point for me. I did not want the client to be dependent on the server. So I started looking at sockets and realised that this is what I want.

With some help from from http://www.dnrtv.com/, I managed to get it working for me. I will post some of the code that I used to get this working. As always, this is the exact code that worked for me and I do not expect you to just copy and paste and have it working for you. You will have to tweek it as per your needs

SERVER code

Public Sub StartServer()
Try
'-- initialize variables
mStopFlag = False
mClients.Clear()
'-- Is the listener still active somehow?
If mListener IsNot Nothing Then
StopServer()
End If
mListenThread = Nothing
'-- get the ip address of the current machine
'-- get the first IP address from the list
Dim lIPAddress As IPAddress = Dns.GetHostEntry(My.Computer.Name).AddressList(0)
'-- get the port from the configuration file
Dim lPort As String = ConfigurationManager.AppSettings("ListenAtPort").ToString
'-- Make sure we have specified an IP Address and Port
If lIPAddress Is Nothing Then
Throw New ApplicationException("The IPAddress is not found. Check your machine settings")
End If
If lPort Is Nothing Then
Throw New ApplicationException("The Port properties cannot be found. Please check the config file.")
End If
'-- get the maximum clients that will be processed by this server
mMaxQueue = Convert.ToInt32(ConfigurationManager.AppSettings("MaxQueue").ToString)
If mMaxQueue = 0 Then
Throw New ApplicationException("The Maximum queue (MaxQueue) cannot be read from the config file")
End If
'-- get the maximum receive buffer from clients
mBufferSize = Convert.ToInt32(ConfigurationManager.AppSettings("BufferSize").ToString)
If mBufferSize = 0 Then
Throw New ApplicationException("The receive buffer size (BufferSize) cannot be read from the config file")
End If
'-- Create a new listener
mListener = New TcpListener(lIPAddress, lPort)
Logger.LogInformation("Listening on " & lIPAddress.ToString & ":" & lPort.ToString)
'-- Start the worker thread for the server
'-- this thread will the server that will listen and accepts new clients
'-- these clients are stored into the collection (arraylist)
mListenThread = New Thread(AddressOf StartServerThread)
mListenThread.Start()
Logger.LogInformation("New server thread started")
'-- This is the thread that listens for incoming messages from connected clients
'-- we plan to receive only one message and that is the order id for
'-- which we need to send the fax
'-- the request is then processed in seperate threads and each thread will respond
'-- back to the client about success or failure
Dim lprocessThread As New Thread(AddressOf ProcessClientRequest)
lprocessThread.Start()
Catch ex As Exception
ExceptionHandler.HandleException(ex)
StopServer() '-- shutdown server if already started
End Try
End Sub
Public Sub StopServer()
Try
mstrCurrentProcedureName = System.Reflection.MethodBase.GetCurrentMethod.Name()
Logger.LogAudit("At the start of StopServer procedure")
mStopFlag = True '-- Tells ProcessClientRequest() to abort
If mListener IsNot Nothing Then
Try
mListener.Stop() '-- Causes StartServerThread to abort
Catch ex As Exception
Logger.LogAudit(mstrCurrentProcedureName + ": error while tring to stop listening - " + ex.Message)
End Try
End If
Thread.Sleep(mThreadSleep) '-- Just to be sure everything is done
SyncLock mClients
'-- Shutdown each client/socket
For Each lClient As Client In mClients
'-- Use a try block in case it's already shut down
Try
If Not lClient Is Nothing Then
lClient.Shutdown()
End If
Catch
'-- if already shutdown, nothing to do. Continue with the loop
End Try
Next
End SyncLock
'-- Clean up
mClients.Clear()
mListener = Nothing
mListenThread = Nothing
mStopFlag = True
'-- Tell the UI we have no more connections
'SafeOnConnectionChange(0)
Catch ex As Exception
ExceptionHandler.HandleException(ex)
Finally
Logger.LogAudit("At the end of StopServer procedure")
End Try
End Sub

#End Region
#Region "Private methods/functions"
'this is the thread for the server that listens at the port and accepts new connections
Private Sub StartServerThread()
Dim lClient As Client
Dim lSocket As Socket
'-- Start listening
mListener.Start()
mStopFlag = False
Try
'-- Loop to handle multiple connection requests
Do
'-- AcceptSocket blocks until a connection is established
lSocket = mListener.AcceptSocket
If Not lSocket Is Nothing Then
'-- lock the list and add the ClientContext
SyncLock mClients
'-- check if the max clients that can be serviced is reached or not
Logger.LogAudit("Received new request: " + DateTime.Now.ToString())
If mClients.Count < lclient =" New" isconnected =" False" deleted =" True" mstopflag =" True" deleted =" False"> 0 Then
If lClient.Status = Client.ClientStatus.Pending Then '-- process only if pending
ProcessClient(lClient)
End If
End If
End If
End If
End If
'-- is the server still up?
If mStopFlag = True Then
Exit Do
End If
'-- sleep to prevent CPU overload
Thread.Sleep(mThreadSleep)
Next
End SyncLock
'-- remove all deleted clients from list
SyncLock mClients
'-- Remove any dead clients - disconnected clients/sockets
Dim removed As Boolean
Dim curCount As Int32 = mClients.Count
Do
removed = False
For i As Int32 = 0 To mClients.Count - 1
If CType(mClients(i), Client).Deleted = True Then
SyncLock mProcessedClients
'-- add the client to the processed client list
mProcessedClients.Insert(mCurrentProcessedOrderCount, mClients(i))
End SyncLock
'-- increment the count and if more than the max then reset
mCurrentProcessedOrderCount += 1
If mCurrentProcessedOrderCount >= mMaxProcessedOrders Then
mCurrentProcessedOrderCount = 1 '-- start overwriting
End If
'-- remove client from main client collection
mClients.Remove(mClients(i))
removed = True
Exit For
End If
Next
Loop Until removed = False
'If clients.Count <> curCount Then
' SafeOnConnectionChange(clients.Count)
'End If
End SyncLock
'-- is the server still up?
'SyncLock stopSyncObj
If mStopFlag = True Then
Exit Do
End If
'End SyncLock
'-- sleep to prevent CPU overload
Thread.Sleep(mThreadSleep)
Loop ' do
Catch ex As Exception
ExceptionHandler.HandleException(ex)
'Again should the server be shutdown when an exception occcurs?
End Try
End Sub

#End Region

CLIENT CODE

Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click
If cbOverride.Checked = True Then
If mCurrentOrderIndex = mOrders.Count Then
If MessageBox.Show("All Orders send. Reset and start again?", "Reset?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
GetOrders()
Connect()
End If
Else
Connect()
End If
Else
Connect()
End If
End Sub
Private Sub Connect()
If btnConnect.Text = "Disconnect" Then
Try
SyncLock ClientSocket
ClientSocket.Shutdown(SocketShutdown.Both)
ClientSocket.Close()
End SyncLock
DisconnectedUI()
Catch
End Try
Else
ClientSocket = New Socket(AddressFamily.InterNetwork, _
SocketType.Stream, ProtocolType.Tcp)
Dim address As IPAddress = _
System.Net.Dns.GetHostEntry(txtAddress.Text).AddressList(0)
Dim endpoint As New IPEndPoint(address, CInt(txtPort.Text))
If cbOverride.Checked = True Then
UpdateStatus(vbCrLf + "Requesting connection for order : " + mOrders(mCurrentOrderIndex).ToString)
Else
UpdateStatus("Requesting connection for order in xml")
End If
ClientSocket.BeginConnect(endpoint, AddressOf Connected, Nothing)
End If
End Sub
Public Sub Connected(ByVal ar As IAsyncResult)
Try
ClientSocket.EndConnect(ar)
'-- Call ConnectedUI
Dim cb As New SimpleCallback(AddressOf ConnectedUI)
Me.Invoke(cb)
' '-- Start Receiving Data
ClientSocket.BeginReceive(recvBuffer, 0, recvBuffer.Length, _
SocketFlags.None, AddressOf ReceivedData, Nothing)
Catch ex As Exception
'-- Call DisconnectedUI
CallDisconnectedUI()
MessageBox.Show(ex.Message)
End Try
End Sub
Public Sub ReceivedData(ByVal ar As IAsyncResult)
Dim numBytes As Int32
Try
SyncLock ClientSocket
numBytes = ClientSocket.EndReceive(ar)
End SyncLock
Catch ex As Exception
CallDisconnectedUI()
Return
End Try
If numBytes = 0 Then
'-- Disconnected!
CallDisconnectedUI()
Return
End If
'-- We have data!
Dim data As String = _
System.Text.ASCIIEncoding.ASCII.GetString(recvBuffer, 0, numBytes)
CallDisplayTextCallback(data)
'-- Start Receiving Data Again!
ClientSocket.BeginReceive(recvBuffer, 0, recvBuffer.Length, _
SocketFlags.None, AddressOf ReceivedData, Nothing)
End Sub
Private Sub CallDisplayTextCallback(ByVal Text As String)
Dim cb As New DisplayTextCallback(AddressOf DisplayText)
Dim args() As Object = {Text}
Me.Invoke(cb, args)
End Sub
Private Delegate Sub DisplayTextCallback(ByVal Text As String)
Public Sub DisplayText(ByVal Text As String)
'txtDisplay.AppendText(Text)
'txtDisplay.SelectionStart = txtDisplay.Text.Length
End Sub
Private Sub CallDisconnectedUI()
Dim cb As New SimpleCallback(AddressOf DisconnectedUI)
Me.Invoke(cb)
End Sub
Private Delegate Sub SimpleCallback()
Public Sub ConnectedUI()
btnConnect.Text = "Disconnect"
btnSend.Enabled = True
Me.AcceptButton = btnSend
UpdateStatus("Connected")
End Sub
Public Sub DisconnectedUI()
btnConnect.Text = "Connect"
btnSend.Enabled = False
Me.AcceptButton = Nothing
End Sub
Private Sub btnSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSend.Click
Try
If cbOverride.Checked = False Then
SendMessage(txtSend.Text)
UpdateStatus("Completed request for order in the order xml")
Else
'tbStatus.Clear()
Dim lSendText As String = txtSend.Text
Dim lxml As New XmlDocument
lxml.LoadXml(lSendText)
lxml.GetElementsByTagName("OrderId").Item(0).InnerXml = mOrders(mCurrentOrderIndex).ToString
SendMessage(lxml.InnerXml)
UpdateStatus("Completed request for order " + mOrders(mCurrentOrderIndex).ToString)
mCurrentOrderIndex += 1
If mCurrentOrderIndex = mOrders.Count Then
MessageBox.Show("All Orders send. Reset and start again if needed.")
End If
'txtSend.Focus()
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub SendMessage(ByVal pMessage As String)
If pMessage.EndsWith(vbCrLf) = False Then
pMessage &= vbCrLf
End If
Dim bytes() As Byte = _
System.Text.ASCIIEncoding.ASCII.GetBytes(pMessage)
SyncLock ClientSocket
ClientSocket.Send(bytes, bytes.Length, SocketFlags.None)
End SyncLock
End Sub

No comments:

Post a Comment