param( [int] $Port = 7788, [string] $Log = "$PSScriptRoot\sc_crier.log" ) function Say($msg) { for ($i = 0; $i -lt 5; $i++) { try { "$msg" | Out-File -FilePath $Log -Append -Encoding utf8; return } catch { Start-Sleep -Milliseconds 100 } } } "" | Out-File -FilePath $Log -Encoding utf8 Say "[crier-sc] starting on 127.0.0.1:$Port" $listener = New-Object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Loopback, $Port) $listener.Server.SetSocketOption('Socket', 'ReuseAddress', $true) $bound = $false for ($i = 0; $i -lt 30 -and -not $bound; $i++) { try { $listener.Start(); $bound = $true } catch { Start-Sleep -Seconds 1 } } if (-not $bound) { Say "[crier-sc] could not bind"; exit 1 } Say "[crier-sc] listening" $client = $listener.AcceptTcpClient() Say "[crier-sc] === shard connected ===" $stream = $client.GetStream() $stream.ReadTimeout = 1500 $reader = New-Object System.IO.StreamReader($stream) $writer = New-Object System.IO.StreamWriter($stream) $writer.AutoFlush = $true Start-Sleep -Milliseconds 500 # Blocking read with a timeout, so StreamReader-buffered lines are not missed the way # checking $stream.DataAvailable does. function Drain($seconds) { $deadline = (Get-Date).AddSeconds($seconds) while ((Get-Date) -lt $deadline) { try { $l = $reader.ReadLine() if ($null -ne $l) { Say "[crier-sc] <- $l" } } catch { Start-Sleep -Milliseconds 50 } } } function Send($obj) { $writer.WriteLine($obj) Say "[crier-sc] -> $obj" Drain 1.5 } # 1. valid add Send '{"kind":"towncrier.add","id":"n1","lines":["Hear ye, hear ye!","The market tax is now 5 percent."],"durationSec":3600}' # 2. add exceeding the line cap (default 6) -> error Send '{"kind":"towncrier.add","id":"n2","lines":["1","2","3","4","5","6","7","8"],"durationSec":60}' # 3. remove the valid one Send '{"kind":"towncrier.remove","id":"n1"}' # 4. remove an unknown id -> error Send '{"kind":"towncrier.remove","id":"does-not-exist"}' Drain 3 Say "[crier-sc] done" $client.Close(); $listener.Stop()