Fundamentals 25 min read

Why Does TCP/UDP Lose Packets? Deep Dive into Causes and Fixes

This article explores the root causes of packet loss in TCP and UDP—including application bugs, kernel buffer limits, network congestion, protocol limitations, and handshake failures—and presents practical solutions such as code optimization, socket tuning, congestion‑control adjustments, and forward error correction techniques.

Deepin Linux
Deepin Linux
Deepin Linux
Why Does TCP/UDP Lose Packets? Deep Dive into Causes and Fixes

Part1 TCP Protocol Packet Loss Reasons

Although TCP provides reliable transmission over unreliable networks, packet loss can still occur when data is missing or dropped during communication. The most common sources are problems in the sending or receiving stages.

When a server sends large amounts of data using frequent

send()

calls, the

send

operation may fail due to logic bugs, inadequate multithread synchronization, or buffer overflows. If the application does not handle these failures, the client receives less data than expected, leading to loss.

1.1 Application‑level mishandling

Logical errors, such as miscalculating the amount of data to send, cause missing data. Multithreaded programs that access shared resources without proper locks can overwrite or drop data, similar to a relay race where runners grab the baton at the same time.

Buffer overflows happen when the amount of data written to the TCP send buffer exceeds its capacity, causing excess data to be discarded—like pouring water into a full cup.

TCP sockets allocate a send buffer and a receive buffer.

When

send(msg)

is called, the packet is copied into the kernel's send buffer rather than being sent directly to the NIC. The receive buffer holds incoming packets until the application reads them.

If the receive buffer becomes full, the TCP receive window shrinks to zero (a “zero‑window” condition) and the sender is instructed to stop sending; any further data will be dropped.

1.2 Poor network conditions

Network congestion, high latency, and link failures all contribute to TCP packet loss. When traffic exceeds a link’s capacity, buffers fill and excess packets are discarded. High latency triggers unnecessary retransmissions, and broken links (e.g., cut fiber) prevent packets from reaching their destination.

1.3 Limitations of TCP mechanisms

TCP’s retransmission and congestion‑control algorithms can fail under severe congestion or prolonged high latency, causing time‑outs and packet drops.

1.4 Packet loss during the three‑way handshake

The

listen

backlog limits how many half‑opened connections can be queued before

accept()

is called. If the backlog is exceeded, SYN packets are dropped. The kernel code handling this looks like:

inet_listen -->>> sk->sk_max_ack_backlog = backlog;
inet_csk_listen_start--->> reqsk_queue_alloc
    for (lopt->max_qlen_log = 3;
         (1 << lopt->max_qlen_log) < nr_table_entries;
         lopt->max_qlen_log++);

During the first handshake, if the SYN queue is full, the packet is dropped:

int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
    if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
        #ifdef CONFIG_SYN_COOKIES
        if (sysctl_tcp_syncookies) {
            want_cookie = 1;
        } else
        #endif
            goto drop;
    }
    ...
    drop:
        return 0;
}

When the SYN queue overflows, the kernel records the event in

/proc/net/snmp

.

Part2 UDP Protocol Packet Loss Reasons

UDP drops packets mainly because the receiver cannot process incoming datagrams quickly enough, causing the kernel to discard excess packets to protect system resources.

Relevant kernel code:

__udp4_lib_rcv--->>> udp_queue_rcv_skb
if ((rc = sock_queue_rcv_skb(sk, skb)) < 0) {
    if (rc == -ENOMEM)
        UDP_INC_STATS_BH(UDP_MIB_RCVBUFERRORS, up->pcflag);
    goto drop;
}

The receive buffer size limits how many packets can be queued; once full, new packets are dropped and the error counters can be seen in

/proc/net/snmp

:

Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors
Udp: 672 0 0 670 0 0

2.1 Connection‑less and unreliable nature

UDP does not establish a handshake nor provide acknowledgments or retransmissions, so any network instability directly results in lost packets.

2.2 Buffer‑related issues

If the receive buffer is too small for the incoming data rate, it fills quickly and subsequent packets are discarded.

2.3 Improper sending strategies

Oversized UDP packets that exceed the MTU are fragmented; loss of any fragment causes the whole packet to be unrecoverable. Sending at excessively high rates also overwhelms the receiver’s buffer.

Part3 TCP Packet Loss Solutions

3.1 Optimize application code

Review and test application logic, fix bugs, and use proper synchronization primitives (e.g., mutexes, semaphores, condition variables) to avoid data races.

Adjust buffer sizes according to workload and release processed data promptly.

3.2 Tune TCP parameters

Increase retransmission attempts and timeout values via

/etc/sysctl.conf

(e.g.,

net.ipv4.tcp_retries2=15

) and select an appropriate congestion‑control algorithm (CUBIC, BBR, RENO).

# Increase retransmission attempts
sudo sysctl -w net.ipv4.tcp_retries2=15

# Increase timeout
sudo sysctl -w net.ipv4.tcp_fin_timeout=30

# View current algorithm
sysctl net.ipv4.tcp_congestion_control

# Set BBR algorithm
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr

3.3 Network optimization measures

Upgrade bandwidth, apply QoS policies, and use monitoring tools (Wireshark, tcpdump) to detect congestion.

# Capture packets with tcpdump
sudo tcpdump -i eth0 -w capture.pcap

Part4 UDP Packet Loss Solutions

4.1 Forward Error Correction (FEC)

FEC adds redundant data to the stream so that lost packets can be reconstructed without retransmission, at the cost of extra bandwidth and latency.

4.2 Application‑level retransmission

Implement timers, sequence numbers, and acknowledgments in the application to resend lost packets, balancing reliability against added traffic.

4.3 Optimize receiver processing

Buffer incoming packets quickly and process them in separate worker threads or asynchronously to avoid buffer overflow.

4.4 Proper socket configuration

Increase the socket receive buffer size with

setsockopt(..., SO_RCVBUF, ...)

and limit packet size to the network MTU. Control sending rate based on feedback from the receiver.

# Example: set receive buffer size
int size = 4*1024*1024; // 4 MiB
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
network troubleshootingTCPsocket programmingUDPpacket loss
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.