Checking for a TCP Listener Connection

I’ve been spending some time re-writing my PoshChat project to replace various things that I feel could have been written better as well as trying to knock out some of the issues that I had in the beginning but just never got around to fixing. One of those items was how I was using the System.Net.Sockets.TcpListener and setting it up to listen for new connections and then handling those connections (usually in another runspace) so I still have the room to handle other new connections.

The ‘Old” Way

The big issue here is that I was using the AcceptTCPClient() method, which presents a blocking call on the console. This means that I cannot do anything else at all, which really wasn’t an issue for me because I had all of this operating in its own runspace. The code snippet below shows the setting up of the runspace that will be handling the listener as well as when connections happen.

$SharedData = [HashTable]::Synchronized(@{})
$Listener = [HashTable]::Synchronized(@{})

#Initial runspace creation to set up server listener 
$NewRunspace = [RunSpaceFactory]::CreateRunspace()
$NewRunspace.Open()
$NewRunspace.SessionStateProxy.setVariable("sharedData", $sharedData)
$NewRunspace.SessionStateProxy.setVariable("Listener", $Listener)
$NewPowerShell = [PowerShell]::Create()
$NewPowerShell.Runspace = $NewRunspace
$sb = {
     $Listener['listener'] = [System.Net.Sockets.TcpListener]15600
     $Listener['listener'].Start()
    while($true) {
    [byte[]]$byte = New-Object byte[] 1024
    $client = $Listener['listener'].AcceptTcpClient()
    If ($client -ne $Null) {
        $stream = $client.GetStream()
        # ... #
    } Else {
        #Connection to server closed
        Break
    }
    }#End While
}
$Handle = $NewPowerShell.AddScript($sb).BeginInvoke()

You can see where I begin using AcceptTCPClient() which presents the blocking call. What happens is that at this point I have no way to break out of this call. Even if I were to change the $True to $False in my While() {} statement, it wouldn’t matter because of the blocking call. Obviously, this presents an issue if I wanted to halt my chat server which would mean completely halting the PowerShell process to effectively stop the listener.

The “New” Way using Pending()

The solution that I have found involves using the Pending() method of the listener object. By still using a loop and instead calling the Pending() method which will return $True for a connection that is attempting to be made to the listener and $False if no connections are being attempted, we can keep from blocking the runspace and allow us to inject a variable update to halt the loop if we wanted to shut down the server. Let’s give it a shot and see what happens.

First we need to start out listener:

#Create the listener and kick it off
$Listener = [System.Net.Sockets.TcpListener]15600 
$Listener.Start()

image

Ok, now if we check for pending connections, we will get back a False, meaning that there are no connections currently being attempted.

$Listener.Pending()

image

Since that is the case, I would simply start a sleep cycle for a second and check again. But now let’s assume that a connection was attempted (using the code below) and we can check again to see what happens.

#Connect to server
$Server = 'Boe-PC'
$Endpoint = new-object System.Net.IPEndpoint ([ipaddress]::any,$SourcePort)
$Client = [Net.Sockets.TCPClient]$endpoint  
$Client.Connect($Server,15600)

Now we can check for pending connection attempts.

image

Looks like we have a connection being made, so we had better act on it by calling the AcceptTCPClient() method. We also can open up a stream during this process as well.

$Client = $Listener.AcceptTcpClient() 
$ClientConnections = $Client  
$ClientStream = $Client.GetStream()         
Write-Verbose ("[$(Get-Date)] New Connection from {0} <{1}>!" -f 
$Client.Client.RemoteEndPoint.Address, $Client.Client.RemoteEndPoint.Port) –Verbos

e

image

Perfect! Now we can begin doing whatever should be done with this new connection. In this case, I am not going to do anything with it, but in PoshChat, this would get handed off to another runspace to handle the client connection and stream for messaging and the listener would be back watching for new connections.

By watching for Pending() connections, we avoid being blocked by the AcceptTCPClient() method and can then easily close our listener up without completely trashing our current PowerShell console.

Hopefully this approach helps you out if you happen to be working with client connections to a listener and provides a way to more easily handle tracking those new connections to the listener without completely taking away your ability to shut down the listener.

This entry was posted in powershell and tagged , , , . Bookmark the permalink.

1 Response to Checking for a TCP Listener Connection

Leave a comment