TCP / UDP Channels
Zeus exposes TCP and UDP transports as IChannel, so protocol and business code do not need to care whether the bytes came from a socket, serial port, or virtual channel.
TCP Client
builder.AddTcpClient("plc", "192.168.1.10", 502);
Use the options overload for custom timeouts:
builder.AddTcpClient("plc", options =>
{
options.Host = "192.168.1.10";
options.Port = 502;
options.ConnectTimeoutMilliseconds = 2000;
});
Port 502 is common for Modbus TCP. Other binary protocols can use the same channel and layer Custom Framing or a protocol package on top.
TCP Server
AddTcpServer listens locally. It fits devices or lower-level controllers that connect to the host application.
builder.AddTcpServer("listener", localPort: 1502);
If localPort is 0, the OS assigns a temporary port:
var listener = (TcpServerChannel)app.Channels.Get("listener");
await app.StartAsync();
Console.WriteLine(listener.LocalEndPoint?.Port);
Parameterless WriteAsync still replies to the most recent client. For multiple clients, use e.RemoteEndPoint or ISessionChannel:
listener.DataReceived += async (_, e) =>
{
if (e.RemoteEndPoint is { } remote)
{
await listener.WriteAsync(remote, e.Data);
return;
}
await listener.WriteAsync(e.Data);
};
Use BroadcastAsync to push to every connected client.
await listener.BroadcastAsync("SYNC"u8.ToArray());
BroadcastAsync observes cancellation. Caller cancellation is rethrown as OperationCanceledException, does not mark the server channel Faulted, and does not drop otherwise connected clients.
UDP Client
AddUdpClient sends to and receives from a fixed remote endpoint:
builder.AddUdpClient("sensor", "192.168.1.20", 1502);
Use options to bind a fixed local port:
builder.AddUdpClient("sensor", options =>
{
options.Host = "192.168.1.20";
options.Port = 1502;
options.LocalPort = 1502;
});
UDP has no connection, delivery guarantee, ordering, or retransmission. Implement request timeouts and retries in the protocol layer when needed.
UDP Server
builder.AddUdpServer("listener", localPort: 1502);
Parameterless WriteAsync still replies to the most recent remote endpoint. For multiple senders, use e.RemoteEndPoint. If no datagram has been received yet, Zeus cannot infer where to send a parameterless response.
JSON Examples
{
"channels": [
{
"name": "plc",
"type": "tcp",
"options": {
"host": "192.168.1.10",
"port": 502
}
},
{
"name": "listener",
"type": "udp-server",
"options": {
"localAddress": "0.0.0.0",
"localPort": 1502
}
}
]
}
If connection or send/receive fails, check the remote listener, firewall, and local port conflicts. During early development, replace the channel with Virtual Channel.