Skip to main content

How can I listen for TCP data

· 4 min read
Jeremy Scott
Co-founder

Opscotch 3.1.1 adds trigger.tcp, allowing workflows to bind to a bootstrap-declared server and process framed TCP input.

The feature is intentionally narrow: the current trigger supports delimiter-based framing or fixed-length framing, with optional batching controls for delimiter-based traffic.

The community repository includes a working example under:

Bootstrap Setup

TCP triggers attach to a server declared in allowHttpServerAccess.

[
{
"deploymentId": "tcp-example",
"remoteConfiguration": "tcp.config.json",
"allowHttpServerAccess": [
{
"id": "tcp-listener",
"port": 13000,
"tcp": true
}
]
}
]

Two things matter here:

  • the workflow trigger references the server by id
  • tcp must be true on that server entry

If the server exists but tcp is not enabled, the runtime raises TT3.

Workflow Trigger Shape

The trigger.tcp schema requires:

  • server
  • exactly one of delimiter or fixedLength

The example workflow in tcp.config.json uses delimiter framing with batching:

{
"workflows": [
{
"name": "TCP Listener",
"steps": [
{
"stepId": "tcp-listener",
"trigger": {
"tcp": {
"server": "tcp-listener",
"delimiter": "|",
"stripDelimiter": true,
"maxBatchWindowMillis": 100,
"batchSize": 5
},
"timer": {
"period": 1000
}
},
"resultsProcessor": {
"resource": "apps/tcp-example/throughput-counter.js"
}
}
]
}
]
}

Multiple Triggers on One Step

The example app is also a valid multiple-trigger pattern.

In Patterns, Opscotch documents that a step can have multiple triggers and will execute when any of them fires. The tcp-example app applies that pattern directly:

  • the tcp trigger ingests framed socket traffic
  • the timer trigger runs once per second

That combination lets one step both receive TCP data and periodically report on accumulated state. In the example app, the TCP path updates counters and the timer path logs throughput.

Framing Modes

Delimiter-Based

Use delimiter when frames are separated by a known byte sequence. The example app uses |:

{
"trigger": {
"tcp": {
"server": "tcp-listener",
"delimiter": "|",
"stripDelimiter": true
}
}
}

stripDelimiter defaults to true, so the emitted payload excludes the delimiter unless you set it to false.

Fixed-Length

Use fixedLength when every frame is the same size:

{
"trigger": {
"tcp": {
"server": "tcp-listener",
"fixedLength": 1024
}
}
}

Batching

Batching is configured directly on trigger.tcp with:

  • batchSize
  • maxBatchWindowMillis

The example app also shows the valid batching fields directly on trigger.tcp:

{
"trigger": {
"tcp": {
"server": "tcp-listener",
"delimiter": "|",
"batchSize": 5,
"maxBatchWindowMillis": 100
}
}
}

Important runtime rule: batching requires delimiter. The implementation rejects batching with fixedLength.

From the API reference:

  • batchSize defaults to 1
  • maxBatchWindowMillis defaults to 0 meaning disabled
  • when batching is used, the workflow payload is emitted as a JSON array string of UTF-8 frame strings

Driving the Example

The included test.sh script sends repeated blobs to 127.0.0.1:13000 using nc. It builds key=value pairs separated by |, which matches the workflow delimiter:

HOST=127.0.0.1
PORT=13000
DELIMITER="|"

printf "%s\n" "$BLOB" | nc -N "$HOST" "$PORT"

The example workflow's resultsProcessor uses the resource apps/tcp-example/throughput-counter.js, and the same step also has a timer trigger so it can periodically report throughput statistics while the TCP trigger keeps ingesting frames.

Error Codes

The TCP trigger error codes in 3.1.1 are:

CodeMeaning
TT1The trigger referenced a bootstrap.allowHttpServerAccess.id that does not exist
TT2Invalid TCP framing configuration
TT3TCP was not permitted on the referenced server
TT4Exception while handling the TCP trigger

In practice, TT2 covers cases such as:

  • neither delimiter nor fixedLength supplied
  • both delimiter and fixedLength supplied
  • batchSize < 1
  • maxBatchWindowMillis < 0
  • batching configured without delimiter

Practical Guidance

Use delimiter framing for line-oriented or record-oriented protocols where batching is useful.

Use fixed-length framing for binary protocols with a stable frame size.