I have been gawking at the networking programming challenges of protohackers for a while. At first I wanted to solve them using AWK but I never go around to it, and moved on to learn other programming languages.
Last week I have been learning a bit of Factor. First programming language of the concatenative family of programming languages that I try.
Among other things I wanted to try it because it seemed to have a very rich stdlib.
You can find my solutions here.
Challenges
00 Smoke Test
An TCP based echo server.
One it's extra requirements
- with no data, it should immediately close connection.
- it should handle half-duplex shutdowns, terminating it, instead of passing on the half-duplex shutdown
01 Prime Time
A server that talks JSON, accepting multiple requests in json format per connection.
$ { jo method=isPrime number=3; jo method=isP; jo method=isPrime number=10; } | nc 127.0.0.1 1234
{"method":"isPrime","prime":true}
{"error":"r2r failed!"}
{"method":"isPrime","prime":false}
02 Means to an End
A per connection, in-memory db, controlled by a custom network binary protocol.
Byte: | 0 | 1 2 3 4 | 5 6 7 8 | Type: | char | int32 (BE) | int32 (BE) | Value: | 'I' | timestamp | price | Value: | 'Q' | mintime | maxtime |
Lessons learned
03 Budget Chat
A TCP based ascii chatroom. With nicks and message status on join/leave.
Unlike the other ones I did this one offline. No second tries.
Luckly Factor has the necessary primitives for this. I choose mailboxes and threads. There are also channels available but, inlike mailboxes they are 2sides blocking.
04 Unusual DB
An UDP based key/value store.
Can't use <threaded-server> with UDP. But found an article implementing a simple udp server.
Unfortunately Factor seems unable to receive empty UDP packets/datagrams. Which are a requirement for this challenge.
05 Mob in the middle
An evil tcp proxy for the chat protocol made on exercise #3. It rewrites some very specific text content.
Hit a couple of "fun" challenges.
- Ensuring both sides of the connection get closed correctly. For that I learned about Factor's destructors.
- TODO Ensuring messages are separated by a newline. This is a more strict requirement than on exercise #3. Seems like is not possible by
readln.- Golang's
bufioReadString('\n') handles this a bit more correctly as it threats EOF without\nas an error. - See
$ echo -n foobarbaz | socat - TCP:127.0.0.1:4321.
- Golang's
- My code still pollutes process stdout with exceptions for closed sockets exceptions not handled…but YOLO.
Dev
Testing
For TCP/UDP:
ncorsocat. I prefersocatdue being non-interactive.$ echo -n "" | nc 127.0.0.1 1234 $ echo -n "" | socat - TCP:127.0.0.1:1234 $ echo -n "" | socat - UDP:127.0.0.1:1234
For UDP: I found
nmapornpinguseful to craft custom packets and see it's output.- nping: in particular, can show the data sent and received back in one command.
$ sudo nping -v3 --bpf-filter='udp and dst port 53' -c 1 --data-string="foo" --udp -p 1234 127.0.0.1 Starting Nping 0.7.80 ( https://nmap.org/nping ) at 2025-11-13 14:12 -03 SENT (0.0107s) UDP [127.0.0.1:53 > 127.0.0.1:1234 len=11 csum=0x275F] IP [ver=4 ihl=5 tos=0x00 iplen=31 id=21414 foff=0 ttl=64 proto=17 csum=0x2926] 0000 45 00 00 1f 53 a6 00 00 40 11 29 26 7f 00 00 01 E...S...@.)&.... 0010 7f 00 00 01 00 35 04 d2 00 0b 27 5f 66 6f 6f .....5....'_foo RCVD (0.0111s) UDP [127.0.0.1:1234 > 127.0.0.1:53 len=15 csum=0xFE22] IP [ver=4 ihl=5 tos=0x00 iplen=35 id=48370 flg=D foff=0 ttl=64 proto=17 csum=0x7fd5] 0000 45 00 00 23 bc f2 40 00 40 11 7f d5 7f 00 00 01 E..#..@.@....... 0010 7f 00 00 01 04 d2 00 35 00 0f fe 22 66 6f 6f 3d .......5..."foo= 0020 62 61 72 bar Max rtt: N/A | Min rtt: N/A | Avg rtt: N/A Raw packets sent: 1 (31B) | Rcvd: 1 (35B) | Lost: 0 (0.00%) Tx time: 0.00115s | Tx bytes/s: 26979.98 | Tx pkts/s: 870.32 Rx time: 1.00159s | Rx bytes/s: 34.94 | Rx pkts/s: 1.00 Nping done: 1 IP address pinged in 1.02 seconds
Conclusion
I solved as much as I think I would enjoy of the problems with Factor. Next on the docket involves juggling a lot more logic. Which I feel I would enjoy more with a more structured language.
It definitely proved that you can solve non-trivial problem with Factor.
So I might fit it to solve things where a stateless bash script won't be enough (I need to talk about this at another time). But where my planned scope won't get too big to handle multiple tuples.