So I made a server in C# which accepts a socket connection as a TCP client.
static void Main(string[] args)
{
TcpListener listener = new TcpListener(Address, PORT);
listener.Start();
var client = listener.AcceptTcpClient();
Console.WriteLine("Client has connected!");
while (true)
{
Console.Write("Powershell Command: ");
var cmd = Console.ReadLine();
client.Client.Send(Encoding.ASCII.GetBytes(cmd));
}
}
And I'm I'm trying to connect to it using Powershell, and it connects just fine.
$tcpConnection = New-Object System.Net.Sockets.TcpClient($args[0], $args[1])
$tcpStream = $tcpConnection.GetStream()
$reader = New-Object System.IO.StreamReader($tcpStream)
$writer = New-Object System.IO.StreamWriter($tcpStream)
$buffer = new-object System.Byte[] 1024
$encoding = new-object System.Text.AsciiEncoding
while ($tcpConnection.Connected)
{
while ($true)
{
$rawresponse = $reader.Read($buffer, 0, 1024)
$rawresponse.Length
$response = $encoding.GetString($buffer, 0, $rawresponse)
Write-Host $response
}
}
$reader.Close()
$writer.Close()
$tcpConnection.Close()
The problem is that when I send a message from the server, powershell prints out
0
so just a 1 and a blank space. Even if I send the string "Test"
Why is that? And how do I properly make it print out the string "Test"
when I send that
CodePudding user response:
There's a couple of subtle bugs in your code:
Buffer Length
First, from the documentation for the StreamReader.Read(Char[], Int32, Int32) method:
Returns
Int32
The number of characters that have been read, or 0 if at the end of the stream and no data was read. The number will be less than or equal to the count parameter, depending on whether the data is available within the stream.
Your $rawresponse
already contains the number of characters written to the buffer, so you don't need to use $rawresponse.Length
. However, just to keep you on your toes PowerShell automatically adds a default Length
and Count
property to all variables if they don't already have one - for example:
PS> $x = 4
PS> $x
4
PS> $x.Length
1
PS> $x.Count
1
so you're outputting PowerShell's "length" of the variable with the line $rawresponse.Length
, not the number of characters written to the buffer. (I can't find a good reference for this behaviour, but I believe it's basically to help with processing objects in a pipeline).
Remove the .Length
and you'll display the correct value.
Buffer Type
The other problem is this line:
$buffer = new-object System.Byte[] 1024
If we go back to the documentation for StreamReader.Read(Char[], Int32, Int32), it says the parameters are:
public override int Read (char[] buffer, int index, int count);
Note the buffer is a char[]
, not a byte[]
. If you tried this in C# you'd get a compiler error, but PowerShell tries to be helpful and will convert your byte[]
variable into a temporary char[]
variable when it invokes the Read
method. It won't, however, convert the result back and put it into the original byte[]
variable afterwards, so any changes made to the temporary buffer by Read
method are lost.
If you change your code to:
$buffer = new-object System.Char[] 1024
You'll get the desired output because the original buffer is passed to the Read
method:
response = 'test'
Buffer Length (again)
As an aside, you should also read your buffer in a loop for each command until Read
returns 0 because there's no guarantee it exhaust the stream in a single read. See the code in the link above for an example...