Files
2026-09-09 23:15:49 +02:00

3.7 KiB

tcp-echo

Minimal .NET 8 TCP echo server. Every reply is prefixed with the instance identifier (INSTANCE_ID env var, falling back to the hostname), and a [<id>] connected line is sent on connect.

Two instances are deployed behind Traefik:

Instance INSTANCE_ID External port
a echo-a 9098
b echo-b 9099

Build and push

Requires a prior docker login gitea.eitczech.com.

./build-push.ps1 -Tag 1.0.0              # pushes gitea.eitczech.com/public/tcp-echo-server:1.0.0
./build-push.ps1 -Tag 1.0.0 -AlsoLatest  # additionally pushes :latest

The manifests reference gitea.eitczech.com/public/tcp-echo-server:latest. To pin a specific tag either edit k8s/instance-a.yaml / k8s/instance-b.yaml, or after kubectl apply -k run:

kubectl -n tcp-echo set image deploy/tcp-echo-a deploy/tcp-echo-b tcp-echo=gitea.eitczech.com/public/tcp-echo-server:1.0.0

If the Gitea registry is private, create a pull secret in the namespace and reference it via imagePullSecrets.

Package description on Gitea

The Dockerfile sets the standard OCI labels (org.opencontainers.image.*). Gitea reads them from the pushed image and shows them on the package page:

Label Shown as / effect
description Package description text
source Links the package to the repository with the same URL (repo must exist first)
url, documentation, licenses, authors, vendor Metadata section on the package page
version, revision, created Filled in by build-push.ps1 from the tag, git commit and current time

To change the description, edit the LABEL in Dockerfile and rebuild. If the package was pushed before the repository existed, link it manually: package page -> Settings -> Link to repository.

Deploy

kubectl apply -k k8s/
kubectl -n tcp-echo rollout status deploy/tcp-echo-a deploy/tcp-echo-b

30-traefik-entrypoint.yaml adds tcpechoa (9098) and tcpechob (9099) entrypoints to the k3s-managed Traefik (Traefik restarts once). For a Helm-managed Traefik, drop that file from kustomization.yaml and run:

helm upgrade traefik traefik/traefik -n kube-system --reuse-values -f k8s/traefik-values.yaml

If Traefik's Service is a LoadBalancer, make sure the cloud/VPS firewall allows TCP 9098 and 9099.

Test

Linux / macOS

$ nc <server-ip> 9098
[echo-a] connected
hello
[echo-a] hello

$ nc <server-ip> 9099
[echo-b] connected
hello
[echo-b] hello

Windows (PowerShell)

Windows has no nc. Use the included script, which opens a TCP connection, prints the greeting, sends a message and prints the echoed reply:

.	est-echo.ps1 -Server <server-ip> -Port 9098
.	est-echo.ps1 -Server <server-ip> -Port 9099 -Message "ping"

Expected output:

Connecting to <server-ip>:9098 ...
<- [echo-a] connected
-> hello
<- [echo-a] hello

Just to check the port is reachable (no echo):

Test-NetConnection -ComputerName <server-ip> -Port 9098

For an interactive session, telnet <server-ip> 9098 works if the Telnet Client feature is enabled (Enable-WindowsOptionalFeature -Online -FeatureName TelnetClient), or install ncat via winget install Insecure.Nmap.

Local test with Docker

docker build -t tcp-echo:local .
docker run --rm -d --name tcp-echo-test -e INSTANCE_ID=echo-local -p 9099:9099 tcp-echo:local
.	est-echo.ps1 -Server localhost -Port 9099
docker rm -f tcp-echo-test