About this article
This article was created using an automated generation workflow powered by generative AI. After reviewing RFC 9293 and official Microsoft Learn documentation for TcpClient, we observe TCP byte streams on localhost and organize the design of boundaries required in actual protocols.Verification Status: 📘 Official Specifications Confirmed, Execution Unverified
In TCP, even if the sender calls Write twice, there is no guarantee that the receiver will get them in two separate parts. This is because TCP handles an ordered byte stream, not a sequence of messages.
Without knowing this difference, it can lead to bugs such as JSON occasionally being cut off in the middle or two pieces of data sticking together.
Try it first
Open two PowerShell windows.
Receiver:
$l = [Net.Sockets.TcpListener]::new(
[Net.IPAddress]::Loopback,
50555
)
$l.Start()
$c = $l.AcceptTcpClient()
$s = $c.GetStream()
$b = New-Object byte[] 1024
$n = $s.Read($b, 0, $b.Length)
[Text.Encoding]::UTF8.GetString($b, 0, $n)
$s.Dispose()
$c.Dispose()
$l.Stop()
Sender:
$c = [Net.Sockets.TcpClient]::new('127.0.0.1', 50555)
$s = $c.GetStream()
foreach ($x in 'HELLO', 'WORLD') {
$b = [Text.Encoding]::UTF8.GetBytes($x)
$s.Write($b, 0, $b.Length)
}
$s.Dispose()
$c.Dispose()
Sometimes it appears as HELLOWORLD, but other times a single Read may only retrieve part of it.
Send count and receive count do not correspond
sequenceDiagram participant C as Client participant T as TCP byte stream participant S as Server C->>T: Write "HELLO" C->>T: Write "WORLD" T-->>S: Read "HELLOWORLD" のこともある T-->>S: Read "HEL" + 次のReadで残り のこともある
TCP guarantees "order" and "reliability," not the write boundaries made by the application.
Try setting the buffer to 3 bytes
By shrinking the receive buffer to 3 bytes, you can visibly see that a single Read cannot retrieve the whole thing.
$b = New-Object byte[] 3
This experiment shows that designing under the assumption that "one Read completes one message" is dangerous.
How to establish boundaries in real protocols
There are three representative methods.
| Method | Example | Characteristics |
|---|---|---|
| Delimiter | One item up to a newline | Suited for text |
| Fixed length | Always 128 bytes | Simple but low flexibility |
| Length prefix | Body length in the first 4 bytes | Suited for binary/APIs |
For example, with newline separation, instead of converting to a string every time you Read, you accumulate data in the receive buffer and extract up to the point where a newline arrives as a single unit.
How it can be applied in practice
TCP socket communication
Custom protocols
Fixed-length messages
Device communication
JSON or CSV flowing over TCP
Integration with legacy enterprise systems
Simply stating that "it could be received in a single attempt every time in the test environment" is not grounds for a correct implementation. Changes in traffic volume, the OS, network conditions, and transmission timing can alter the split units.
Points to note
Read()The return valuenrepresents the number of bytes actually receivedThe buffer size is not necessarily filled to the maximum
Read() = 0is an important state indicating the end of a connectionBecause UTF-8 characters span multiple bytes, character boundaries must also be considered
Determine the maximum message length in production protocols
Summary
TCP is a byte stream and does not preserve message boundaries
Write counts and Read counts do not match
Boundary rules are required on the application side
Use newlines, fixed lengths, length prefixes, etc.
Properly handle Read return values and connection termination
