"People obsess over sending packets. Real wizards control ACKs."

Welcome to the next level of TCP socket programming – where you don't just send data, you orchestrate the acknowledgment cadence of the remote peer.

In this post, we're cracking open one of the weirdest and most unpredictable yet powerful TCP socket options on Linux: TCP_QUICKACK. It's not well-documented, it's not intuitive, and it's often wrongly described – which makes it perfect for us.

TL;DR;

  • TCP_QUICKACK disables delayed ACKs temporarily.
  • It tells the kernel to send ACKs immediately for received packets, without waiting.
  • It's not a setting – it's a one-time signal.
  • It resets itself after one ACK (or a few).
  • It's meant for acknowledgment pacing, especially in request/response protocols.
  • And yes, it's Linux-only.

Historical Background: Delayed ACKs

Back in the early days of TCP (RFC 1122, 1989), engineers observed that too many ACKs were flooding networks. TCP's default behavior was to ACK every segment. But in a request/response protocol (e.g., Telnet, HTTP/1.0), this leads to ACK storms.

To mitigate this, Delayed ACK was introduced:

Wait ~40ms before sending an ACK – unless more data comes in or you have a reason to reply sooner.

This small delay helps amortize ACKs and coalesce responses, reducing packet count.

But it backfires in latency-sensitive protocols. Consider this:

Client:  [GET /foo] --->
Server:           [waits 40ms…] ---> [ACK]

That's 40ms of nothing. Wasted time. Multiply that by millions of small requests and you've just throttled throughput hard.

Enter: TCP_QUICKACK (Linux-only)

In 2004, the Linux kernel added TCP_QUICKACK. Its purpose: allow applications to request immediate ACKs from the peer – temporarily disabling Delayed ACK behavior.

It's defined in <netinet/tcp.h>:

#define TCP_QUICKACK 12

How It Works (Actually)

Let's be clear: this is not a switch you "turn on". It's more like whispering to the kernel:

"Hey… for the next ACK or two… don't delay it."

Key facts:

  • TCP_QUICKACK is one-shot.
  • You can call it repeatedly to keep ACKs flowing fast.
  • It only affects incoming data – i.e., how you ACK others.
  • You can combine it with TCP_NODELAY and TCP_CORK.
  • It only works on SOCK_STREAM (TCP) sockets.

Real-World Example: Disabling Delayed ACKs After a Write

Let's say you're building a latency-sensitive RPC server.

When you receive a small request and send a quick reply, you don't want the client delaying their ACK (which might block the next write due to cwnd limits or BBR pacing).

Tell the kernel: ACK the next segment now, not later.

#include <netinet/tcp.h>

int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(flag));

Boom. The next ACK is sent immediately.

Want to do it every time? Just call setsockopt() before every read() or after every write().

Let's Measure It

Run this code with tcpdump and observe the ACK behavior:

char buf[1024];
read(sockfd, buf, sizeof(buf)); // receive request

int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(flag));

write(sockfd, "OK\n", 3);

Without TCP_QUICKACK, the ACK may be delayed by 40ms.

With it, the ACK fires instantly – and the client can proceed faster.

It's a surgical latency optimization.

Use Case: Self-Tuning HTTP/1.1 Server

Write a server that toggles QUICKACK only for small responses — large responses don't need it because they'll naturally cause ACKs due to stream pressure.

if (response_size < 128) {
    int q = 1;
    setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &q, sizeof(q));
}
write(sockfd, response, response_size);

This makes your server dynamically ACK-tuned, like nginx on caffeine.

Kernel Dive

Want to see the magic?

Check out tcp_input.c in the Linux source:

if (inet_csk_ack_scheduled(sk)) {
    if (quickack) {
        tcp_send_ack(sk);
        inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
    } else {
        inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, ...)
    }
}

The quickack variable flips an internal flag icsk_ack.quick, which forces ack_mode = TCP_ACK_NOW.

It's a manual override of Delayed ACK logic.

Quantum-Level Thought: Controlling the Other Side

Want to get crazy?

Use TCP_QUICKACK to manipulate how your ACKs are seen by the sender, and how that affects their congestion control and RTT estimation.

Faster ACKs:

  • Reduce RTT estimation errors.
  • Speed up cwnd growth in slow start.
  • Improve fairness in BBR-based flows.
setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));

This is how you game the sender's control loop. Pure wizardry.

TCP_QUICKACK vs TCP_NODELAY vs TCP_CORK

Let's disambiguate the Three Horsemen of Socket Tuning:

Socket Option  What It Controls             Direction  Behavior
TCP_NODELAY    Send-side Nagle disabling    Outgoing   Immediate write
TCP_CORK       Send-side packet coalescing  Outgoing   Delay write
TCP_QUICKACK   Receive-side ACK delay       Incoming   Immediate ACK

Use them together:

  • TCP_CORK to batch sending.
  • TCP_QUICKACK to speed up the remote.
  • TCP_NODELAY to burst small frames.

Advanced Trick: RTT-Probing with QUICKACK

You can use QUICKACK to get precise RTT samples for the other side:

setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
gettimeofday(&start, NULL);
recv(sockfd, buf, sizeof(buf));
gettimeofday(&end, NULL);

Because QUICKACK disables delay, your ACK to their last packet happens ASAP. Now end - start reflects actual delivery time, not delayed kernel ACK fudge.

This is how you instrument latency with surgical precision.

Does It Work on All Kernels?

Nope.

It was added in Linux ~2.4.3, but behavior changed over time. It may:

  • Not work on loopback (ACKs may already be instant).
  • Get ignored by some congestion control algorithms.
  • Behave differently if socket is in CLOSE_WAIT or TIME_WAIT.

Test before you trust. Always measure.

Gotchas

TCP_QUICKACK is ephemeral. You need to reapply it constantly.

  • It's not available on BSD, macOS, or Windows.
  • Only affects ACK behavior – doesn't guarantee immediate reading.
  • Doesn't disable Delayed ACK globally – only affects current state.

Can be a CPU tax under high QPS due to ACK burst.

Final Thoughts

People obsess over sending TCP data fast. But true network wizards know that ACK timing controls throughput just as much – sometimes even more.

With TCP_QUICKACK, you can:

  • Force low-latency acks.
  • Tune request/response loops.
  • Control peer cwnd behavior.
  • Accurately measure RTT.
  • Design protocols with precise ACK semantics.

You don't just "send packets". You shape reality.

Further Reading

  • Linux source: net/ipv4/tcp_input.c, tcp_output.c
  • RFC 1122: Requirements for Internet Hosts
  • Wireshark: Filter tcp.analysis.ack_rtt
  • Read: BBR congestion control

Closing

Want more socket sorcery?

Next up: TCP_NOTSENT_LOWAT, TCP_SAVE_SYN, or the zero-copy loopback stack.

Just say the word.