Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Asynchronous Sockets - Close and re-init?

Status
Not open for further replies.

Moebius01

Programmer
Oct 27, 2000
309
0
0
US
Let me preface this by saying I'm a novice coder at best. I've been tossed into doing this due to circumstances beyond control, so please forgive any ignorance.

I'm working on a listener service to work with the Avaya phone system as my company is switching to it. After poking around, I ended up basing most of my code on MS's example for asynchronous sockets. So far so good. Now, I'd like to add some manner of code when a timeout occurs (no data from Avaya for 5 minutes) to close any open connections and sockets and re-initialize to start listening again. The problem is, I'm unsure how to deal with the sockets in a multithreaded situation. Specifically how to determine if there are any open ports or connections, or any socket receiving data. The code thus far looks like this:

Code:
Public Shared allDone As New ManualResetEvent(False)
Public Shared listener As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

Public Shared Sub StartListening()

        ' Data buffer for incoming data.
        Dim bytes() As Byte = New [Byte](1024) {}
        ' Establish Listener Endpoint Info
        Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName())
        localip = ipHostInfo.AddressList(0)
        Dim localEndPoint As New IPEndPoint(localip, localport)
        ' Bind the socket to the local endpoint and listen for incoming connections.
        Try
            listener.Bind(localEndPoint)
            listener.Listen(100)

            While True
                ' Set the event to nonsignaled state.
                allDone.Reset()
                ' Start an asynchronous socket to listen for connections.
                If logging >= 1 Then
                    objStreamWriter = New StreamWriter(logpath & "collectorlog.txt", True)
                    objStreamWriter.WriteLine(Now() & " - Waiting for a connection...")
                    objStreamWriter.Close()
                End If
                listener.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), listener)
                ' Wait until a connection is made before continuing.
                allDone.WaitOne()
            End While

        Catch e As Exception
            If logging >= 1 Then
                objStreamWriter = New StreamWriter(logpath & "collectorlog.txt", True)
                objStreamWriter.WriteLine(Now() & " - Error occured : " & e.ToString())
                objStreamWriter.Close()
            End If
        End Try
End Sub 'StartListening
Public Shared Sub AcceptCallback(ByVal ar As IAsyncResult)
        If logging >= 1 Then
            objStreamWriter = New StreamWriter(logpath & "collectorlog.txt", True)
            objStreamWriter.WriteLine(Now() & " - Connection accepted.")
            objStreamWriter.Close()
        End If
        ' Signal the main thread to continue.
        allDone.Set()
        ' Get the socket that handles the client request.
        Dim listener As Socket = CType(ar.AsyncState, Socket)
        Dim handler As Socket = listener.EndAccept(ar)

        ' Create the state object.
        Dim state As New StateObject
        state.workSocket = handler
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
End Sub 'AcceptCallback
Public Shared Sub ReadCallback(ByVal ar As IAsyncResult)

        Dim content As [String] = [String].Empty
        ' Retrieve the state object and the handler socket from the asynchronous state object.
        Dim state As StateObject = CType(ar.AsyncState, StateObject)
        Dim handler As Socket = state.workSocket
        ' Read data from client socket. 
        Dim bytesRead As Integer = handler.EndReceive(ar)
        If bytesRead > 0 Then
            ' There might be more data, so store the data received so far.
            state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead))
            ' Check for end-of-file tag ("END_OF_RECORDS"). If not present, continue read.
            content = state.sb.ToString()
            If content.IndexOf("END_OF_RECORDS") > -1 Then
                ' All the data has been read from the client.
                If logging >= 2 Then
                    objStreamWriter = New StreamWriter(logpath & "collectorlog.txt", True)
                    objStreamWriter.WriteLine(Now() & " - Data Received.")
                    objStreamWriter.Close()
                End If
                ' Update packettime to now.  Timer checks this.
                packettime = Now()
                ' Send to parser/db insert module
                db.dbUpdate(content)

                ' Clear State and Content Objects
                state = New StateObject
                content = Nothing
                bytesRead = Nothing
                state.workSocket = handler
                ' Respawn receiver for next packet
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)

                ' Terminate this thread.
                Thread.CurrentThread.Abort()
                allDone.Close()
            Else
                ' Not all data received. Get more.
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
            End If
        End If
End Sub 'ReadCallback

I've created a timer component to monitor the packettime variable for delays greater than 30 seconds and log an error (data should arrive every 10). At 5 minutes, I'd like to re-initialize the listener, but I'm not sure how to achieve that in the multithreaded environment. Can someone help me with an example of how to close any open ports and sockets, and I'm assuming just re-call the StartListening() sub? So far, any method I've tried to make sure all the sockets are closed just causes the service to hang, and just trying to call StartListening again results in errors because the port is in use.
 
Add a DateTime to your state object instance, and set it's value to the current time on your receive callback method.

You should already have a collection of sockets or state objects (needed when your service shuts down), so every few minutes (on another thread) iterate through them, looking for ones that haven't received anything in over 5 minutes. If you find one, just close the socket.

One other thing you'll need to do is pre-allocate all your send and receive byte array buffers (just create a singleton class to hold them all). This is because these buffers get "pinned" into place in memory by the CLR when you make an async call. When the garbage collector runs, it starts at the top of memory and works it's way down. As soon as it hits a pinned piece of memory, it stops compacting. Over time, as more sockets are opened (and their buffers pinned), you'll experience higher and higher memory consumption.

Chip H.


____________________________________________________________________
Donate to Katrina relief:
If you want to get the best response to a question, please read FAQ222-2244 first
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top