Skip to content


Pode has a generic TCP server type inbuilt. Unlike the other server types, the TCP server lets you build pretty much anything over TCP. The TCP server supports non-TLS, as well as implicit and explicit TLS endpoints.

Where a web server using Routes, a TCP server uses Verbs - think of them like "commands" or "phrases". Requests sent will be matched to a Verb, and that Verb's logic invoked (more info below).


This server type is still in the early days, so if you find any bugs or have any suggestions, please feel free to raise them over on GitHub or Discord! 😄


To create a TCP server you need to create an appropriate Endpoint with the TCP protocol, plus some Verbs.

The following example will create a TCP endpoint listening on localhost:9000, and creates a simple Verb to respond back to a connected client:

Start-PodeServer {
    Add-PodeEndpoint -Address localhost -Port 9000 -Protocol Tcp -CRLFMessageEnd -Acknowledge 'Welcome!'

    Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
        Write-PodeTcpClient -Message 'HI'


If you use telnet to test/send data to a TCP server, ensure that the Endpoint has the -CRLFMessageEnd switch. If missed, the server will receive data in single characters.

Start the above server, and then in a different terminal start telnet and send "HELLO" - pressing Enter to send:

$> telnet localhost 9000
S> Welcome!

When you send "HELLO", the server will respond with "HI". If you can't use telnet, then there's a quick test script below to use.


If you're using telnet to send data, backspaces will be recorded as sent data.


If you'd like to send an initial message to clients as soon as they connect, you can set an -Acknowledge message on the Endpoint:

Add-PodeEndpoint -Address localhost -Port 9000 -Protocol Tcp -CRLFMessageEnd -Acknowledge 'Welcome!'

If you connect via telnet now:

$> telnet localhost 9000
S> Welcome!


Verbs work like Routes, whereby you can setup many of them and Pode will invoke the Verb's logic that best matches the data sent. Data sent usually should be in a structured format, such as <COMMAND> <PARAMETERS> - akin to SMTP and FTP. However, data can be unstructured, and this is where the wildcard Verb comes in handy.

To create a Verb you use Add-PodeVerb:

Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
    Write-PodeTcpClient -Message 'HI'


Also similar to Routes, Verbs support parameters in the format :<name>. These parameters will be available in $TcpEvent.Parameters:

Add-PodeVerb -Verb 'HELLO :username' -ScriptBlock {
    Write-PodeTcpClient -Message "HI, $($TcpEvent.Parameters.username)"


The wildcard Verb, denoted via -Verb *, is a catch-all for any data that doesn't match other defined Verbs. You could use this Verb to write back to the client that they sent invalid data:

Add-PodeVerb -Verb * -ScriptBlock {
    Write-PodeTcpClient -Message 'Unrecognised verb sent'

Or, you can access the data sent by the client via either $TcpEvent.Request.RawBody or $TcpEvent.Request.Body. RawBody is a byte array of the data, where as Body is a UTF8 decoded string of the RawBody. This should allow you to then parse any freestyle data as you see fit, such as a simple web server:

Start-PodeServer {
    Add-PodeEndpoint -Address localhost -Port 9000 -Protocol Tcp

    Add-PodeVerb -Verb * -Close -ScriptBlock {
        $TcpEvent.Request.Body | Out-Default
        Write-PodeTcpClient -Message "HTTP/1.1 200 `r`nConnection: close`r`n`r`n<b>Hello, there</b>"

Running the above server and navigating to http://localhost:9000 will greet you with a message. The raw data sent by the browser will be display on the terminal; this could be parsed to do different things.

SSL Upgrade

If you're using explicit TLS on your TCP server, then at some point you'll want to upgrade the connection to using SSL. There are two ways of achieving this; one is to use a simple command verb, and the -UpgradeToSsl switch:

Add-PodeVerb -Verb 'STARTTLS' -UpgradeToSsl

The second way is to call the upgrade method directly:

Add-PodeVerb -Verb 'STARTTLS' -ScriptBlock {
    Write-PodeTcpClient -Message 'TLS GO AHEAD'


At some point you'll likely need to close the connection from the server side. There are two ways of achieving this; one is to use a simple command verb, and the -Close switch:

Add-PodeVerb -Verb 'QUIT' -Close

The second way is to call Close-PodeTcpClient directly:

Add-PodeVerb -Verb 'QUIT' -ScriptBlock {
    Write-PodeTcpClient -Message 'Bye!'

Read Data

In the above examples you've seen Write-PodeTcpClient being used. This function simply sends data back to a connected client, but what if we want to read data? Sometimes, instead of ending a Verb's logic and letting Pode wait for the next data, you might want to receive this data yourself in a Verb. For this there is Read-PodeTcpClient:

Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
    Write-PodeTcpClient -Message "Hi! What's your name?"
    $name = Read-PodeTcpClient -CRLFMessageEnd
    Write-PodeTcpClient -Message "Hi, $($name)!"


You can enable TLS for your endpoints by supplying the normal relevant certificates parameters on Add-PodeEndpoint, and setting the -Protocol to Tcps. You can also toggle between implicit and explicit TLS by using the -TlsMode parameter.

By default the TLS mode is implicit, and the default port for implicit TLS is 9002; for explicit TLS it's 9003. These can of course be customised using -Port.

Start-PodeServer {
    Add-PodeEndpoint -Address * -Protocol Tcps -SelfSigned -TlsMode Explicit
    Add-PodeEndpoint -Address * -Protocol Tcps -SelfSigned -TlsMode Implicit

    Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
        Write-PodeTcpClient -Message 'HI'



Verbs will be passed the $TcpEvent object, that contains the Request, Response, and other properties:

Name Type Description
Request object The raw Request object
Response object The raw Response object
Lockable hashtable A synchronized hashtable that can be used with Lock-PodeObject
Endpoint hashtable Contains the Address and Protocol of the endpoint being hit - such as "" or "", or HTTP or HTTPS for the Protocol
Parameters hashtable Contains the parsed parameter values from the Verb's path
Timestamp datetime The current date and time of the Request

Test Send

The following function can be used to test sending messages to a TCP server. This is a modified version of the function found here.

function Send-TCPMessage($Endpoint, $Port, $Message) {
    # Setup connection
    $Address = [System.Net.IPAddress]::Parse([System.Net.Dns]::GetHostAddresses($EndPoint))
    $Socket = New-Object System.Net.Sockets.TCPClient($Address,$Port)

    # Setup stream wrtier
    $Stream = $Socket.GetStream()
    $Writer = New-Object System.IO.StreamWriter($Stream)

    # Write message to stream

    # Close connection and stream
    Start-Sleep -Seconds 1


Send-TCPMessage -Port 9000 -EndPoint -Message "HELLO"


If you have the -CRLFMessageEnd switch specified, you'll need to add `r`n to the end of the -Message