I'm having trouble writing a PowerShell script that can both send and receive data via a TCP connection. It only seems to let me do one or the other.
Below is what I have so far. I want to listen and wait for a connection, then once established, receive a string containing an IP address, do some fancy lookup to see what user is logged into that machine, then send back the username. If I only send data, it works. If I only receive data, it works. If I try to do both, only the receive works. What am I doing wrong?
$port = 1234
do {
$user = ""
$endpoint = new-object System.Net.IPEndPoint ([system.net.ipaddress]::any, $port)
$listener = new-object System.Net.Sockets.TcpListener $endpoint
$listener.start()
$client = $listener.AcceptTcpClient() # will block here until connection
$stream = $client.GetStream();
$reader = New-Object System.IO.StreamReader $stream
$writer = New-Object System.IO.StreamWriter $stream
$add = $reader.ReadLine()
#$reader.close()
write-host "Request from " $add
if($add) {
$user = & wmic /Node:$add ComputerSystem Get UserName
write-host "User returned is " $user[2]
}
if($user[2] -eq "ERROR:") {
$user[2] = "ErrNoUserW"
} elseif(!$user[2]) {
$user[2] = "ErrServerW"
}
$writer.Write($user[2])
#$writer.close()
$stream.close()
$client.close()
$listener.stop()
} while(1)
I thought that the stream could only be read or write at any given time. Have you tried closing the stream then re-opening it?
Related
Using a PowerShell script I will have to read and write to a console. We write an input and wait for an out that will be captured by $Reader.ReadLine(). But in some cases there wont be any output to get captured for the reader in that case the reader needs to look data from the stream and if there is no data the ReadLine() gets stuck/blocked waiting for the data from the console stream, whereas we need the ReadLine() to just wait for 5 seconds. If there is no data, it needs to get timed out and move on to the next command.
Please let me know whether there is any way to timeout $Reader.ReadLine() in PowerShell?
I see that in Java/C# we can use $Reader.ReadLine(1000) to timeout after 1 second but that doesn't seem to be working on PowerShell.
$tcpConnection = New-Object System.Net.Sockets.TcpClient($Computername, $Port)
$tcpStream = $tcpConnection.GetStream()
$reader = New-Object System.IO.StreamReader($tcpStream)
$writer = New-Object System.IO.StreamWriter($tcpStream)
$writer.AutoFlush = $true
$buffer = New-Object System.Byte[] 1024
$encoding = New-Object System.Text.AsciiEncoding
while ($tcpStream.DataAvailable) {
$reader.ReadLine()
}
if ($tcpConnection.Connected) {
$writer.WriteLine($username)
$reader.ReadLine()
$writer.WriteLine($password)
$reader.ReadLine()
try {
# if there is any data it will show here if there is no data then
# it should get timed out after 5 seconds
$Reader.ReadLine()
} catch {
Write-Host "Login Failed"
}
}
I would say that you should read this post C# Stream.Read with timeout
Converting that to your code sample, should end up with something like this.
$tcpConnection = New-Object System.Net.Sockets.TcpClient($Computername, $Port)
#This is one way you could try
$tcpConnection.ReceiveTimeout = 5000;
$tcpStream = $tcpConnection.GetStream()
$reader = New-Object System.IO.StreamReader($tcpStream)
$writer = New-Object System.IO.StreamWriter($tcpStream)
$writer.AutoFlush = $true
$buffer = New-Object System.Byte[] 1024
$encoding = New-Object System.Text.AsciiEncoding
while ($tcpStream.DataAvailable) {
$reader.ReadLine()
}
if ($tcpConnection.Connected) {
$writer.WriteLine($username)
$reader.ReadLine()
$writer.WriteLine($password)
$reader.ReadLine()
try {
# if there is any data it will show here if there is no data then
# it should get timed out after 5 seconds
$Reader.ReadLine()
} catch {
Write-Host "Login Failed"
}
}
Take it for a spin and let me know if it works or not.
Updated:
Updated to reflect the code to only contain the working solution.
I need help with a Powershell TCP client that can monitor when the server closes the connection. The starting point for this code is from here and looks like this:
## Connect-Computer.ps1
## Interact with a service on a remote TCP port
param(
[string] $remoteHost = "localhost",
[int] $port = 23
)
try
{
## Open the socket, and connect to the computer on the specified port
write-host "Connecting to $remoteHost on port $port"
$socket = new-object System.Net.Sockets.TcpClient($remoteHost, $port)
if($socket -eq $null) { return; }
$stream = $socket.GetStream()
$writer = new-object System.IO.StreamWriter($stream)
$buffer = new-object System.Byte[] 1024
$encoding = new-object System.Text.AsciiEncoding
while($true)
{
## Allow data to buffer for a bit
start-sleep -m 500
## Read all the data available from the stream, writing it to the
## screen when done.
while($stream.DataAvailable)
{
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
## Read the user's command, quitting if they hit ^D
$command = read-host
## Write their command to the remote host
$writer.WriteLine($command)
$writer.Flush()
}
}
finally
{
## Close the streams
$writer.Close()
$stream.Close()
}
The problem is when the server closes the port the client continues to wait. How can I use a buffered read but still check if the port is still open? I dont need to know immediately if the port closes, just after a few minutes.
Thanks in advance for any help.
I use this powershell script for getting output stream of telnet request.
Function Get-Telnet {
Param (
[Parameter(ValueFromPipeline=$true)]
[string[]]$Commands = #(),
[string]$RemoteHost = "0.0.0.0",
[string]$Port = "23",
[int]$WaitTime = 1000,
[string]$OutputPath = "C:\temp\telnet_output.log"
)
#Attach to the remote device, setup streaming requirements
$Socket = New-Object System.Net.Sockets.TcpClient($RemoteHost, $Port)
if ($Socket) {
$Stream = $Socket.GetStream()
$Writer = New-Object System.IO.StreamWriter($Stream)
$Buffer = New-Object System.Byte[] 1024
$Encoding = New-Object System.Text.AsciiEncoding
#Now start issuing the commands
foreach ($Command in $Commands) {
$Writer.WriteLine($Command)
$Writer.Flush()
Start-Sleep -Milliseconds $WaitTime
}
#All commands issued, but since the last command is usually going to be
#the longest let's wait a little longer for it to finish
Start-Sleep -Milliseconds ($WaitTime * 4)
$Result = ""
#Save all the results
while ($Stream.DataAvailable) {
$Read = $Stream.Read($Buffer, 0, 1024)
$Result += ($Encoding.GetString($Buffer, 0, $Read))
}
} else {
$Result = "Unable to connect to host: $($RemoteHost):$Port"
}
#Done, now save the results to a file
#$Result | Out-File $OutputPath
$Result
}
This works for a Cisco switch and a telnet server, but for the Alcatel OmniPCX and the HPUX the stream reading is empty.
I need a named pipe to read and write.
In a program I create the pipe server using from kernel32.dll:
string PipeName = "\\\\.\\pipe\\myMT4";
int PipeMode = PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT; # tried too: PIPE_NOWAIT
int hPipe = CreateNamedPipeW(
PipeName,
PIPE_ACCESS_DUPLEX,
PipeMode,
PIPE_UNLIMITED_INSTANCES,1024,1024,
NMPWAIT_USE_DEFAULT_WAIT,NULL);
The handle hPipe is valid - every things seems to be ok here!
But in a PowerShell-script I want to open a client, connect and open the writer -
and cannot connect => timed out
function connect{
Param ([PSObject] $h)
...
$h.Pipe = New-Object -TypeName System.IO.Pipes.NamedPipeClientStream "\\.\pipe\PipeTest"
$h.Pipe.Connect( 5000 )
$h.Writer = New-Object -TypeName System.IO.StreamWriter $h.Pipe, $h.Encode
I really would prefer this way to have a similar access when reading and writen
either from/to the pipe and sockets e.g.:
function write{
Param ([PSObject] $h, [string] $line )
try {
$h.Writer.Write($line)
}
What is wrong?
Thanks in advance,
Gooly.
PS:
It seems that the program cannot deal with pipe-servers - I have to open a pipe-client, and that works but that causes other problems:
I define for the PowerShell-pipe-server :
$pipeName = "testpipe"
$pipeDir = [System.IO.Pipes.PipeDirection]::InOut
$pipeMsg = [System.IO.Pipes.PipeTransmissionMode]::Message
$pipeOpti = [System.IO.Pipes.PipeOptions]::Asynchronous
$pipe = New-Object system.IO.Pipes.NamedPipeServerStream(
$pipeName, $pipeDir, 1, $pipeMsg, $pipeOpti )
$pipe.WaitForConnection() #
$sw = new-object System.IO.StreamWriter $pipe
$sw.AutoFlush = $true
$sw.WriteLine("Server pid is $pid")
$sw.Dispose()
$pipe.Dispose()
1) My first problem is now that the powerShell-pipe-server is blocked by
$pipe.WaitForConnection()
until a client connects but it must handle 2 different sockets independently meanwhile and
2) if the client closes the connection I was unable to tell the client to open the same pipe again and the client gets the Windows-error: ERROR_PIPE_BUSY 231
Form the program I connect to the server with the kernel32.dll-function:
int CallNamedPipeW(string PipeName,
string outBuffer, int outBufferSz,
uint& inBuffer[], int inBufferSz,
int& bytesRead[], int timeOut
);
Any ideas?
Hmm, I can get named pipes to work between two different PowerShell sessions so I don't think it is an inherent PowerShell limitation:
Here is the server script:
$pipe = new-object System.IO.Pipes.NamedPipeServerStream 'testpipe','Out'
$pipe.WaitForConnection()
$sw = new-object System.IO.StreamWriter $pipe
$sw.AutoFlush = $true
$sw.WriteLine("Server pid is $pid")
$sw.Dispose()
$pipe.Dispose()
Here is the client script:
$pipe = new-object System.IO.Pipes.NamedPipeClientStream '.','testpipe','In'
$pipe.Connect()
$sr = new-object System.IO.StreamReader $pipe
while (($data = $sr.ReadLine()) -ne $null) { "Received: $data" }
$sr.Dispose()
$pipe.Dispose()
Client outputs:
Received: Server pid is 22836
I´m using a normal powershell httplistener script.
The script listenes on port 80 and gives an response.
Now I tried to handle more than one request as the same time. The problem is that the second respons has to wait until the first response was finished by the script.
I tried to start an own job for every http-request - but I can´t send a response to the listener from the PS-Job.
Does anyone know, how to handle parallel httprequests in PS?
Here is the Script I´m using:
$url = 'http://localhost/'
$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add($url)
$listener.Start()
Write-Host "Listening at $url..."
while ($listener.IsListening)
{
$context = $listener.GetContext()
$requestUrl = $context.Request.Url
$response = $context.Response
Write-Host ''
Write-Host "> $requestUrl"
$localPath = $requestUrl.LocalPath
$route = $routes.Get_Item($requestUrl.LocalPath)
if ($route -eq $null)
{
$response.StatusCode = 404
}
else
{
$content = & $route
$buffer = [System.Text.Encoding]::UTF8.GetBytes($content)
$response.ContentLength64 = $buffer.Length
$response.OutputStream.Write($buffer, 0, $buffer.Length)
}
$response.Close()
$responseStatus = $response.StatusCode
Write-Host "< $responseStatus"
}
What is the right way to handle more than one request at the same time?
Thanks #all!
You can use multiple runspaces (even the runspace pool) within the same PowerShell process. See this blog post for details on how to do that.
Check the $request.url.LocalPath or another one of the url properties depending on how much of context you are looking for.
Provide a different response depending on the request.
$htmlout = "<html><link rel=""stylesheet"" href=""MyStleSheet.css""><body class=""body"" />Hello World</body></html>"
$css = ".body {background-color: white;font-size: 20px;font-family: calibri;}"
while($Listener.IsListening -and $noBreak){
$Context = $listener.GetContext()
$request = $context.request
if($request.url.LocalPath -match '.css')
{
$buffer = [System.Text.Encoding]::utf8.getbytes($css)
}
if($request.url.LocalPath -match '.html')
{
$buffer = [System.Text.Encoding]::utf8.getbytes($htmlout)
}
$response = $context.response
$response.contentlength64 = $buffer.length
$response.OutputStream.Write($buffer,0,$buffer.length)
}