Et simpelt eksempel på brug af NamedPipeServerStream

Hvis man vil lave en server og en klient som snakker sammen via pipes, har jeg her sammensat et lille eksempel, som viser hvad man skal bruge, for at komme igang.

Server

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Main
{
    class Program
    {
        static System.Threading.ManualResetEvent _quitEvent = new System.Threading.ManualResetEvent(false);
        static object state = new object();
        static void Main(string[] args)
        {
            Console.CancelKeyPress += (sender, eArgs) => {
                _quitEvent.Set();
                eArgs.Cancel = true;
            };
            var server = new System.IO.Pipes.NamedPipeServerStream("server", System.IO.Pipes.PipeDirection.InOut, 
                1,
                System.IO.Pipes.PipeTransmissionMode.Message,
                System.IO.Pipes.PipeOptions.Asynchronous);


           var result=  server.BeginWaitForConnection((asyncResult) => 
            {
                server.EndWaitForConnection(asyncResult);
                var reader = new System.IO.StreamReader(server);
                Console.WriteLine(reader.ReadLine());

            }, state);
			
            _quitEvent.WaitOne();
            server.Close();
        }
    }
}

Det man skal huske på serversiden, hvis man laver wait kaldet asynkront via server.BeginWaitForConnection er at man skal huske at kalde EndWaitForConnection så hurtigt man kan efter man modtager noget data. Vil man lave det synkront kan man nøjes med at kalde WaitForConnection. Dette kald er et blocking kald, som frigiver tråden, og venter på at der kommer en connection, før tråden bliver vækket til live igen.

Pga. at NamedPipeServerStream er en wrapper omkring en system pipe, skal man huske på at man ikke bare kan lave en while(true), og modtage flere connections over samme begin, som her

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Main
{
    class Program
    {
        static System.Threading.ManualResetEvent _quitEvent = new System.Threading.ManualResetEvent(false);
        static object state = new object();
        static void Main(string[] args)
        {
            Console.CancelKeyPress += (sender, eArgs) => {
                _quitEvent.Set();
                eArgs.Cancel = true;
            };

            var server = new System.IO.Pipes.NamedPipeServerStream("server", System.IO.Pipes.PipeDirection.InOut, 
                1,
                System.IO.Pipes.PipeTransmissionMode.Message,
                System.IO.Pipes.PipeOptions.Asynchronous);

			while(true)
			{
				var result=  server.WaitForConnection();
				
				server.EndWaitForConnection(asyncResult);
				var reader = new System.IO.StreamReader(server);
				Console.WriteLine(reader.ReadLine());
			}
            _quitEvent.WaitOne();

            server.Close();
        }
    }
}

Dette vil give en fejl! Man skal have var server = new ... med ind i løkken.

Klient

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var client = new System.IO.Pipes.NamedPipeClientStream("server");
            client.Connect();
            string message = "hello world";
            client.Write(Encoding.UTF8.GetBytes(message), 0, message.Length);
            client.Close();
        }
    }
}

Klienten er lige ud af landevejen. Husk at kalde Connect() før man laver et Write!!

Skrevet af Martin Slot den 9/22/2016 2:01:54 PM


Powershell remoting

Det er forholdsvis simpel at lave en remote session i PowerShell, man skal dog først lave noget opsætning:

Start af PSRemoting

For at kunne starte en remote session skal der startes en service. Fyr op for en powershell konsol, som administrator og kør følgende i prompten:

Enable-PSRemoting -Force

Da denne kommando tilføjer en firewall regel, til indgående trafik anbefales der at man sætter noget IP restriction på. Dette kan gøres fra prompten:

Set-Item wsman:\localhost\client\trustedhosts *

* gør at alle kan remote ind, hvilket jeg ikke vil anbefale. Man kan udskifte * med en liste af kommasepareret ip adresser for at opnå en form for restriktion.

Test af forbindelse

I mit tilfælde vil jeg gerne kunne styre min farm af Azure servere (Jeg har tre til at køre denne blog). For at kunne gøre dette skal man huske at åbne i den ydre firewall også! Ellers åbner man kun i den interne, hvilket gør at serverne kan nå hinanden på den interne VPN (hvis man har sat dette op). Udefra er der lukket, så husk at åbne her!

Man kan nu teste forbindelsen med

Test-WsMan [IP eller host]

Oprettelse af forbindelse

Meldes der ok, er man klar til at fyre op for en session:

Enter-PSSession -ComputerName [IP eller host] -Credential [brugernavn]

Ønsker man kun at fyre script blokke af, er det også muligt via Invoke-Command

Invoke-Command -ComputerName [IP eller host] -ScriptBlock { COMMAND } -credential [brugernavn]

Brug af SSL

Pga en manglende wildcard certifikat til cloudapp.net kan det godt være lidt svært, hvis man vil bruge -UseSSL når man laver en Enter-PSSession. For at få dette til at virke, har jeg været nød til at følge denne https://blogs.endjin.com/2014/03/a-step-by-step-guide-to-connecting-to-an-azure-virtual-machine-with-powershell-remoting. Man skal lave nogle krumspring, dog virker det i sidste ende.

Skrevet af Martin Slot den 8/12/2016 12:12:00 PM


Custom extensions til Powershell

I mit daglige arbejde bruger jeg powershell en del pga git. Jeg surfer derfor en del rundt i et givent projekt og,

En gang imellem har jeg dog brug for at starte en explorer i det directory jeg står i, da den visuelle repræsentation nogen gange er mere effektiv end hvad en ls kan give mig. Jeg fik derfor stykket denne "oneliner" sammen

explorer.exe (Get-Item -Path ".\" -Verbose).FullName

"onelineren" er dog en del at skulle skrive hver gang jeg gerne vil åbne en explorer.exe i nuværende directory, derfor har jeg lavet en funktion, EC, og tilføjet den til min profile. Har man ikke en profile for den indloggede bruger, kan man hurtigt oprette en ved at køre følgende kommando

New-Item -path $profile -type file –force

Accepter prompten, som springer frem på skærmen, og man vil derefter have en ny fil, Microsoft.Powershell_profile.ps1 i folderen WindowsPowerShell, som ligger under My Documents. Den fulde sti til profile filen, vil være C:\Users\[Username]\Documents\WindowsPowerShell\Microsoft.Powershell_profile.ps1.

Heri har jeg smidt følgende funktion

function EC {
    explorer.exe (Get-Item -Path ".\" -Verbose).FullName
}

Hvis jeg lukker alle min powershells ned, og åbner powershell igen, vil jeg, ved at skrive EC i prompten, få åbnet en explorer i den directory jeg står i. simpelt.

Denne funktion tager højest sandsynlig ikke højde for alt (funktionen tager faktisk ikke højde for noget), så man kan sikkert nemt få den til at fejle. Dog er jeg stadig en rookie når det kommer til Powershell scripting, så funktionen kan nok let laves mere robust. Finder jeg fejl, vil jeg selvfølgelig lave et opfølgende indlæg, hvor jeg kommer med en opdateret funktion.

Skrevet af Martin Slot den 8/11/2016 12:00:00 PM