TCP vs UDP Packet Fragmentation: Can Packets Arrive in Pieces?


2 views

When working with network programming, understanding how TCP and UDP handle packet delivery is crucial. Let's examine whether packets can arrive fragmented at the receiver side.

TCP is a stream-oriented protocol that doesn't guarantee message boundaries. When you send 20 bytes:


// TCP client example
send(socket_fd, buffer, 20, 0);

The receiver might get:


// TCP server might receive in multiple chunks
recv(socket_fd, buffer, 20, 0); // returns 10
recv(socket_fd, buffer + 10, 10, 0); // returns remaining 10

Key points about TCP:

  • No guaranteed message boundaries
  • Data might arrive in multiple pieces
  • Application must handle reassembly

UDP is datagram-oriented and maintains message boundaries:


// UDP client example
sendto(socket_fd, buffer, 20, 0, &server_addr, addr_len);

The receiver will either:


// UDP server receives complete datagrams
recvfrom(socket_fd, buffer, 20, 0, &client_addr, &addr_len); // returns 20 or error

Important UDP behaviors:

  • Datagrams arrive complete or not at all
  • No fragmentation at application layer
  • IP layer may fragment large packets (> MTU)

For TCP applications, you need to implement message framing. Common approaches include:


// Example: Prefix with length
uint32_t length = htonl(20);
send(socket_fd, &length, sizeof(length), 0);
send(socket_fd, buffer, 20, 0);

For UDP, while the protocol preserves message boundaries, you should:


// Check for truncated datagrams
ssize_t received = recvfrom(/*...*/);
if (received == -1) {
    // Handle error
} else if (received < expected) {
    // Handle truncated packet (rare for UDP)
}

Both protocols can experience IP fragmentation when packets exceed the MTU:

  • TCP: Handled transparently by the stack
  • UDP: Fragmentation occurs but receiver sees complete datagram

To avoid fragmentation issues:


// Get MTU size
int mtu;
socklen_t len = sizeof(mtu);
getsockopt(socket_fd, IPPROTO_IP, IP_MTU, &mtu, &len);

Choose based on application needs:

Factor TCP UDP
Message Boundaries No Yes
Fragmentation Handling Automatic Manual
Reliability High Low

When working with network protocols, developers often need to understand the atomicity guarantees of packet delivery. Let's examine how TCP and UDP handle this differently at the transport layer.

TCP is a stream-oriented protocol, meaning it doesn't preserve message boundaries. When you send 20 bytes:

// Sender code
send(socket_fd, buffer, 20, 0);

The receiver might get the data in multiple chunks:

// Receiver code
int received = recv(socket_fd, buffer, BUFFER_SIZE, 0);
// First call might return 10 bytes
// Second call might return remaining 10 bytes

Key characteristics of TCP:

  • No guaranteed atomic delivery of the exact sent chunk size
  • Data may be fragmented by IP layer (MTU limitations)
  • Application must implement buffering and message reconstruction

UDP maintains message boundaries - each send() corresponds to exactly one recv():

// UDP sender
sendto(socket_fd, buffer, 20, 0, &server_addr, addrlen);

// UDP receiver
recvfrom(socket_fd, buffer, BUFFER_SIZE, 0, &client_addr, &addrlen);
// Will receive complete 20-byte datagram or nothing at all

UDP guarantees:

  • Atomic delivery of complete datagrams
  • No partial datagrams delivered to application
  • Either the entire packet arrives or it doesn't

For TCP applications, you'll need to implement message framing. Common approaches include:

// Length-prefixed messaging
uint32_t message_length = htonl(20);
send(socket_fd, &message_length, sizeof(uint32_t), 0);
send(socket_fd, buffer, 20, 0);

// Receiver side
uint32_t message_length;
recv(socket_fd, &message_length, sizeof(uint32_t), MSG_WAITALL);
message_length = ntohl(message_length);
char* buffer = malloc(message_length);
recv(socket_fd, buffer, message_length, MSG_WAITALL);

For UDP, the main concern is staying within path MTU (typically ~1500 bytes) to avoid IP fragmentation.

The IP layer may fragment packets exceeding the MTU, but this is transparent to applications:

  • TCP: Fragmented packets are reassembled before delivery to application
  • UDP: If any fragment is lost, the entire datagram is discarded

You can check the DF (Don't Fragment) flag behavior:

int optval = 1;
setsockopt(socket_fd, IPPROTO_IP, IP_MTU_DISCOVER, &optval, sizeof(optval));